Package org.rssowl.ui.internal.editors.feed

Source Code of org.rssowl.ui.internal.editors.feed.NewsContentProvider

/*   **********************************************************************  **
**   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.ui.internal.editors.feed;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.rssowl.core.persist.IBookMark;
import org.rssowl.core.persist.IFeed;
import org.rssowl.core.persist.IMark;
import org.rssowl.core.persist.INews;
import org.rssowl.core.persist.ISearchMark;
import org.rssowl.core.persist.dao.DynamicDAO;
import org.rssowl.core.persist.event.NewsAdapter;
import org.rssowl.core.persist.event.NewsEvent;
import org.rssowl.core.persist.event.NewsListener;
import org.rssowl.core.persist.event.SearchMarkAdapter;
import org.rssowl.core.persist.event.SearchMarkEvent;
import org.rssowl.core.persist.event.runnable.EventType;
import org.rssowl.core.persist.reference.BookMarkReference;
import org.rssowl.core.persist.reference.FeedLinkReference;
import org.rssowl.core.persist.reference.NewsReference;
import org.rssowl.core.persist.reference.SearchMarkReference;
import org.rssowl.core.persist.service.PersistenceException;
import org.rssowl.ui.internal.EntityGroup;
import org.rssowl.ui.internal.EntityGroupItem;
import org.rssowl.ui.internal.util.JobRunner;
import org.rssowl.ui.internal.util.UIBackgroundJob;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* @author bpasero
*/
public class NewsContentProvider implements ITreeContentProvider {
  private NewsBrowserViewer fBrowserViewer;
  private NewsTableViewer fTableViewer;
  private NewsGrouping fGrouping;
  private NewsListener fNewsListener;
  private SearchMarkAdapter fSearchMarkListener;
  private IMark[] fInput;
  private FeedView fFeedView;
  private boolean fDisposed;

  /* Cache displayed News */
  private Set<INews> fCachedNews;

  /**
   * @param tableViewer
   * @param browserViewer
   * @param feedView
   */
  public NewsContentProvider(NewsTableViewer tableViewer, NewsBrowserViewer browserViewer, FeedView feedView) {
    fTableViewer = tableViewer;
    fBrowserViewer = browserViewer;
    fFeedView = feedView;
    fGrouping = feedView.getGrouper();
  }

  /*
   * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
   */
  public Object[] getElements(Object inputElement) {
    List<Object> elements = new ArrayList<Object>();

    /* Wrap into Object Array */
    if (!(inputElement instanceof Object[]))
      inputElement = new Object[] { inputElement };

    /* Foreach Object */
    Object[] objects = (Object[]) inputElement;
    for (Object object : objects) {

      /* This is a News */
      if (object instanceof INews && ((INews) object).isVisible()) {
        elements.add(object);
      }

      /* This is a NewsReference */
      else if (object instanceof NewsReference) {
        NewsReference newsRef = (NewsReference) object;
        INews news = obtainFromCache(newsRef);
        if (news != null)
          elements.add(news);
      }

      /* This is a FeedReference */
      else if (object instanceof FeedLinkReference) {
        synchronized (NewsContentProvider.this) {
          Collection<INews> news = fCachedNews;
          if (news != null) {
            if (fGrouping.getType() == NewsGrouping.Type.NO_GROUPING)
              elements.addAll(news);
            else
              elements.addAll(fGrouping.group(news));
          }
        }
      }

      /* This is a Bookmark */
      else if (object instanceof BookMarkReference) {
        synchronized (NewsContentProvider.this) {
          Collection<INews> news = fCachedNews;
          if (news != null) {
            if (fGrouping.getType() == NewsGrouping.Type.NO_GROUPING)
              elements.addAll(news);
            else
              elements.addAll(fGrouping.group(news));
          }
        }
      }

      /* This is a SearchMark */
      else if (object instanceof SearchMarkReference) {
        synchronized (NewsContentProvider.this) {
          Collection<INews> news = fCachedNews;
          if (news != null) {
            if (fGrouping.getType() == NewsGrouping.Type.NO_GROUPING)
              elements.addAll(news);
            else
              elements.addAll(fGrouping.group(news));
          }
        }
      }

      /* This is a EntityGroup */
      else if (object instanceof EntityGroup) {
        EntityGroup group = (EntityGroup) object;

        List<EntityGroupItem> items = group.getItems();
        for (EntityGroupItem item : items) {
          if (((INews) item.getEntity()).isVisible())
            elements.add(item.getEntity());
        }
      }
    }

    return elements.toArray();
  }

  /*
   * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
   */
  public Object[] getChildren(Object parentElement) {
    List<Object> children = new ArrayList<Object>();

    /* Handle EntityGroup */
    if (parentElement instanceof EntityGroup) {
      List<EntityGroupItem> items = ((EntityGroup) parentElement).getItems();
      for (EntityGroupItem item : items)
        children.add(item.getEntity());
    }

    return children.toArray();
  }

  /*
   * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
   */
  public Object getParent(Object element) {

    /* Handle Grouping specially */
    if (fGrouping.isActive() && element instanceof INews) {
      List<EntityGroup> groups = fGrouping.group(Collections.singletonList((INews) element));
      if (groups.size() == 1)
        return groups.get(0);
    }

    return null;
  }

  /*
   * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
   */
  public boolean hasChildren(Object element) {
    return element instanceof EntityGroup;
  }

  /*
   * @see org.eclipse.jface.viewers.IContentProvider#dispose()
   */
  public void dispose() {
    fDisposed = true;
    unregisterListeners();
  }

  /*
   * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
   * java.lang.Object, java.lang.Object)
   */
  public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
  /* Ignore - Input changes are handled via refreshCache(Object input) */
  }

  boolean isGroupingEnabled() {
    return fGrouping.getType() != NewsGrouping.Type.NO_GROUPING;
  }

  /* Returns the news that have been added since the last refresh */
  synchronized List<INews> refreshCache(IMark[] input, boolean onlyAdd) throws PersistenceException {
    List<INews> addedNews = Collections.emptyList();

    /* Update Input */
    fInput = input;

    /* Register Listeners if not yet done */
    if (fNewsListener == null)
      registerListeners();

    /* Clear old Data if required */
    if (fCachedNews == null)
      fCachedNews = new HashSet<INews>();
    else if (!onlyAdd)
      fCachedNews.clear();

    /* Check if ContentProvider was already disposed */
    if (fDisposed)
      return addedNews;

    /* Obtain the News */
    addedNews = new ArrayList<INews>();
    for (IMark mark : input) {

      /* Obtain this BookMark's Feed */
      if (mark instanceof IBookMark) {
        IFeed feed = ((IBookMark) mark).getFeedLinkReference().resolve();
        addedNews.addAll(feed.getVisibleNews());
      }

      /* Obtain this SearchMark's News */
      else if (mark instanceof ISearchMark) {
        ISearchMark searchMark = (ISearchMark) mark;
        List<NewsReference> matchingNews = searchMark.getResult();
        for (NewsReference newsRef : matchingNews) {

          /* Avoid to resolve an already shown News */
          if (onlyAdd && hasCachedNews(newsRef))
            continue;

          /* Resolve and Add News */
          INews resolvedNews = newsRef.resolve();
          if (resolvedNews != null) //TODO Remove once Bug 173 is fixed
            addedNews.add(resolvedNews);
        }
      }
    }

    /* Add into Cache */
    fCachedNews.addAll(addedNews);

    return addedNews;
  }

  synchronized Set<INews> getCachedNews() {
    return fCachedNews;
  }

  synchronized boolean hasCachedNews() {
    return fCachedNews != null && !fCachedNews.isEmpty();
  }

  private synchronized boolean hasCachedNews(NewsReference ref) {
    if (fCachedNews == null)
      return false;

    for (INews news : fCachedNews) {
      if (ref.references(news))
        return true;
    }

    return false;
  }

  private synchronized INews obtainFromCache(NewsReference ref) {
    for (INews cachedNews : fCachedNews) {
      if (ref.references(cachedNews))
        return cachedNews;
    }

    return null;
  }

  private void registerListeners() {

    /* Saved Search Listener */
    fSearchMarkListener = new SearchMarkAdapter() {
      @Override
      public void resultsChanged(final Set<SearchMarkEvent> events) {
        for (SearchMarkEvent event : events) {
          ISearchMark searchMark = event.getEntity();

          for (final IMark inputMark : fInput) {
            if (inputMark.equals(searchMark)) {
              JobRunner.runUIUpdater(new UIBackgroundJob(fFeedView.getEditorControl()) {
                private List<INews> fAddedNews;

                @Override
                protected void runInBackground(IProgressMonitor monitor) {
                  fAddedNews = refreshCache(new IMark[] { inputMark }, true);
                }

                @Override
                protected void runInUI(IProgressMonitor monitor) {

                  /* Event not interesting for us or we are disposed */
                  if (fAddedNews == null || fAddedNews.size() == 0)
                    return;

                  /* Refresh Viewer to reflect changes */
                  fFeedView.refresh(true, true); //TODO Seems some JFace Caching Problem here

                  /* Add to Browser-Viewer if showing entire Feed */
                  if (fBrowserViewer.getInput() instanceof BookMarkReference)
                    fBrowserViewer.add(fBrowserViewer.getInput(), fAddedNews.toArray());
                }
              });

              /* Done */
              return;
            }
          }
        }
      }
    };

    DynamicDAO.addEntityListener(ISearchMark.class, fSearchMarkListener);

    /* News Listener */
    fNewsListener = new NewsAdapter() {

      /* News got Added */
      @Override
      public void entitiesAdded(final Set<NewsEvent> events) {
        JobRunner.runInUIThread(fFeedView.getEditorControl(), new Runnable() {
          public void run() {
            List<INews> addedNews = null;

            /* Filter News which are from a different Feed than displayed */
            for (NewsEvent event : events) {
              if (isInputRelatedTo(event.getEntity(), EventType.PERSIST)) {
                if (addedNews == null)
                  addedNews = new ArrayList<INews>();

                INews news = event.getEntity();
                addedNews.add(news);

                /* Update Cache */
                synchronized (NewsContentProvider.this) {
                  fCachedNews.add(news);
                }
              }
            }

            /* Event not interesting for us or we are disposed */
            if (addedNews == null || addedNews.size() == 0)
              return;

            /* Ask Group */
            if (fGrouping.needsRefresh(INews.class, events, false)) {
              fFeedView.refresh(true, false);
              return;
            }

            /* Add to Table-Viewer if Visible */
            if (fFeedView.isTableViewerVisible())
              fTableViewer.add(fTableViewer.getInput(), addedNews.toArray());

            /* Add to Browser-Viewer if showing entire Feed */
            if (fBrowserViewer.getInput() instanceof BookMarkReference)
              fBrowserViewer.add(fBrowserViewer.getInput(), addedNews.toArray());
          }
        });
      }

      /* News got Updated */
      @Override
      public void entitiesUpdated(final Set<NewsEvent> events) {
        JobRunner.runInUIThread(fFeedView.getEditorControl(), new Runnable() {
          public void run() {
            List<INews> deletedNews = null;
            List<INews> updatedNews = null;

            /* Filter News which are from a different Feed than displayed */
            for (NewsEvent event : events) {
              if (isInputRelatedTo(event.getEntity(), EventType.UPDATE)) {
                INews news = event.getEntity();

                /* News got Deleted */
                if (!news.isVisible()) {
                  if (deletedNews == null)
                    deletedNews = new ArrayList<INews>();
                  deletedNews.add(news);

                  synchronized (NewsContentProvider.this) {
                    fCachedNews.remove(news);
                  }
                }

                /* News got Updated */
                else {
                  if (updatedNews == null)
                    updatedNews = new ArrayList<INews>();
                  updatedNews.add(news);
                }
              }
            }

            /* Event not interesting for us or we are disposed */
            if (deletedNews == null && updatedNews == null)
              return;

            /* News got Deleted */
            if (deletedNews != null && !isGroupingEnabled()) {

              /* Remove from Table-Viewer */
              if (fFeedView.isTableViewerVisible())
                fTableViewer.remove(deletedNews.toArray());

              /* Remove from Browser-Viewer */
              if (contains(fBrowserViewer.getInput(), deletedNews))
                fBrowserViewer.remove(deletedNews.toArray());
            }

            /* Ask Group */
            if (fGrouping.needsRefresh(INews.class, events, true))
              fFeedView.refresh(false, false);

            /* News got Updated */
            if (updatedNews != null) {

              /* Update in Table-Viewer */
              if (fFeedView.isTableViewerVisible())
                fTableViewer.update(updatedNews.toArray(), null);

              /* Update in Browser-Viewer */
              if (contains(fBrowserViewer.getInput(), updatedNews))
                fBrowserViewer.update(updatedNews.toArray(), null);
            }
          }
        });
      }

      /* News got Deleted */
      @Override
      public void entitiesDeleted(final Set<NewsEvent> events) {
        JobRunner.runInUIThread(fFeedView.getEditorControl(), new Runnable() {
          public void run() {
            List<INews> deletedNews = null;

            /* Filter News which are from a different Feed or invisible */
            for (NewsEvent event : events) {
              INews news = event.getEntity();
              if (news.isVisible() && isInputRelatedTo(news, EventType.REMOVE)) {
                if (deletedNews == null)
                  deletedNews = new ArrayList<INews>();
                deletedNews.add(news);

                synchronized (NewsContentProvider.this) {
                  fCachedNews.remove(news);
                }
              }
            }

            /* Event not interesting for us or we are disposed */
            if (deletedNews == null)
              return;

            /* News got Deleted */
            if (!isGroupingEnabled()) {

              /* Remove from Table-Viewer */
              if (fFeedView.isTableViewerVisible())
                fTableViewer.remove(deletedNews.toArray());

              /* Remove from Browser-Viewer */
              if (contains(fBrowserViewer.getInput(), deletedNews))
                fBrowserViewer.remove(deletedNews.toArray());
            }

            /* Ask Group */
            if (fGrouping.needsRefresh(INews.class, events, false))
              fFeedView.refresh(false, false);
          }
        });
      }
    };

    DynamicDAO.addEntityListener(INews.class, fNewsListener);
  }

  private void unregisterListeners() {
    DynamicDAO.removeEntityListener(INews.class, fNewsListener);
    DynamicDAO.removeEntityListener(ISearchMark.class, fSearchMarkListener);
  }

  private boolean isInputRelatedTo(INews news, EventType type) {
    for (IMark mark : fInput) {

      /* Check if BookMark references the News' Feed */
      if (mark instanceof IBookMark) {
        IBookMark bookmark = (IBookMark) mark;
        if (bookmark.getFeedLinkReference().equals(news.getFeedReference()))
          return true;
      }

      /* TODO This is a workaround! */
      else if (type != EventType.PERSIST && mark instanceof ISearchMark) {
        return true;
      }
    }

    return false;
  }

  private boolean contains(Object input, List<INews> list) {

    /* Can only belong to this Feed since filtered before already */
    if (input instanceof BookMarkReference)
      return true;

    /* TODO Handle searchmarks properly */
    else if (input instanceof SearchMarkReference)
      return true;

    else if (input instanceof INews)
      return list.contains(input);

    else if (input instanceof EntityGroup) {
      List<EntityGroupItem> items = ((EntityGroup) input).getItems();
      for (EntityGroupItem item : items) {
        if (list.contains(item.getEntity()))
          return true;
      }
    }

    else if (input instanceof Object[]) {
      Object inputNews[] = (Object[]) input;
      for (Object inputNewsItem : inputNews) {
        if (list.contains(inputNewsItem))
          return true;
      }
    }

    return false;
  }
}
TOP

Related Classes of org.rssowl.ui.internal.editors.feed.NewsContentProvider

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.