PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/rssowl-2.0.5/org.rssowl.ui/src/org/rssowl/ui/internal/editors/feed/NewsContentProvider.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 819 lines | 516 code | 150 blank | 153 comment | 203 complexity | a24e74a801919d723147209337d322e0 MD5 | raw file
  1. /* ********************************************************************** **
  2. ** Copyright notice **
  3. ** **
  4. ** (c) 2005-2009 RSSOwl Development Team **
  5. ** http://www.rssowl.org/ **
  6. ** **
  7. ** All rights reserved **
  8. ** **
  9. ** This program and the accompanying materials are made available under **
  10. ** the terms of the Eclipse Public License v1.0 which accompanies this **
  11. ** distribution, and is available at: **
  12. ** http://www.rssowl.org/legal/epl-v10.html **
  13. ** **
  14. ** A copy is found in the file epl-v10.html and important notices to the **
  15. ** license from the team is found in the textfile LICENSE.txt distributed **
  16. ** in this package. **
  17. ** **
  18. ** This copyright notice MUST APPEAR in all copies of the file! **
  19. ** **
  20. ** Contributors: **
  21. ** RSSOwl Development Team - initial API and implementation **
  22. ** **
  23. ** ********************************************************************** */
  24. package org.rssowl.ui.internal.editors.feed;
  25. import org.eclipse.core.runtime.IProgressMonitor;
  26. import org.eclipse.jface.viewers.ITreeContentProvider;
  27. import org.eclipse.jface.viewers.Viewer;
  28. import org.eclipse.swt.widgets.Tree;
  29. import org.eclipse.swt.widgets.TreeItem;
  30. import org.rssowl.core.persist.IBookMark;
  31. import org.rssowl.core.persist.IEntity;
  32. import org.rssowl.core.persist.IFolder;
  33. import org.rssowl.core.persist.IMark;
  34. import org.rssowl.core.persist.INews;
  35. import org.rssowl.core.persist.INewsBin;
  36. import org.rssowl.core.persist.INewsMark;
  37. import org.rssowl.core.persist.ISearchMark;
  38. import org.rssowl.core.persist.dao.DynamicDAO;
  39. import org.rssowl.core.persist.event.NewsAdapter;
  40. import org.rssowl.core.persist.event.NewsEvent;
  41. import org.rssowl.core.persist.event.NewsListener;
  42. import org.rssowl.core.persist.event.SearchMarkAdapter;
  43. import org.rssowl.core.persist.event.SearchMarkEvent;
  44. import org.rssowl.core.persist.event.runnable.EventType;
  45. import org.rssowl.core.persist.reference.BookMarkReference;
  46. import org.rssowl.core.persist.reference.FeedLinkReference;
  47. import org.rssowl.core.persist.reference.ModelReference;
  48. import org.rssowl.core.persist.reference.NewsBinReference;
  49. import org.rssowl.core.persist.reference.NewsReference;
  50. import org.rssowl.core.persist.reference.SearchMarkReference;
  51. import org.rssowl.core.persist.service.PersistenceException;
  52. import org.rssowl.ui.internal.Controller;
  53. import org.rssowl.ui.internal.EntityGroup;
  54. import org.rssowl.ui.internal.EntityGroupItem;
  55. import org.rssowl.ui.internal.FolderNewsMark;
  56. import org.rssowl.ui.internal.FolderNewsMark.FolderNewsMarkReference;
  57. import org.rssowl.ui.internal.util.JobRunner;
  58. import org.rssowl.ui.internal.util.UIBackgroundJob;
  59. import java.util.ArrayList;
  60. import java.util.Collection;
  61. import java.util.Collections;
  62. import java.util.HashSet;
  63. import java.util.List;
  64. import java.util.Set;
  65. /**
  66. * @author bpasero
  67. */
  68. public class NewsContentProvider implements ITreeContentProvider {
  69. private final NewsBrowserViewer fBrowserViewer;
  70. private final NewsTableViewer fTableViewer;
  71. private final NewsGrouping fGrouping;
  72. private final NewsFilter fFilter;
  73. private NewsListener fNewsListener;
  74. private SearchMarkAdapter fSearchMarkListener;
  75. private INewsMark fInput;
  76. private final FeedView fFeedView;
  77. private boolean fDisposed;
  78. /* Cache displayed News */
  79. private Set<INews> fCachedNews;
  80. /**
  81. * @param tableViewer
  82. * @param browserViewer
  83. * @param feedView
  84. */
  85. public NewsContentProvider(NewsTableViewer tableViewer, NewsBrowserViewer browserViewer, FeedView feedView) {
  86. fTableViewer = tableViewer;
  87. fBrowserViewer = browserViewer;
  88. fFeedView = feedView;
  89. fGrouping = feedView.getGrouper();
  90. fFilter = feedView.getFilter();
  91. }
  92. /*
  93. * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
  94. */
  95. public Object[] getElements(Object inputElement) {
  96. List<Object> elements = new ArrayList<Object>();
  97. /* Wrap into Object Array */
  98. if (!(inputElement instanceof Object[]))
  99. inputElement = new Object[] { inputElement };
  100. /* Foreach Object */
  101. Object[] objects = (Object[]) inputElement;
  102. for (Object object : objects) {
  103. /* This is a News */
  104. if (object instanceof INews && ((INews) object).isVisible()) {
  105. elements.add(object);
  106. }
  107. /* This is a NewsReference */
  108. else if (object instanceof NewsReference) {
  109. NewsReference newsRef = (NewsReference) object;
  110. INews news = obtainFromCache(newsRef);
  111. if (news != null)
  112. elements.add(news);
  113. }
  114. /* This is a FeedReference */
  115. else if (object instanceof FeedLinkReference) {
  116. synchronized (NewsContentProvider.this) {
  117. Collection<INews> news = fCachedNews;
  118. if (news != null) {
  119. if (fGrouping.getType() == NewsGrouping.Type.NO_GROUPING)
  120. elements.addAll(news);
  121. else
  122. elements.addAll(fGrouping.group(news));
  123. }
  124. }
  125. }
  126. /* This is a class that implements IMark */
  127. else if (object instanceof ModelReference) {
  128. Class<? extends IEntity> entityClass = ((ModelReference) object).getEntityClass();
  129. if (IMark.class.isAssignableFrom(entityClass) || IFolder.class.isAssignableFrom(entityClass)) { //Suppoer FolderNewsMark too
  130. synchronized (NewsContentProvider.this) {
  131. Collection<INews> news = fCachedNews;
  132. if (news != null) {
  133. if (fGrouping.getType() == NewsGrouping.Type.NO_GROUPING)
  134. elements.addAll(news);
  135. else
  136. elements.addAll(fGrouping.group(news));
  137. }
  138. }
  139. }
  140. }
  141. /* This is a EntityGroup */
  142. else if (object instanceof EntityGroup) {
  143. EntityGroup group = (EntityGroup) object;
  144. List<EntityGroupItem> items = group.getItems();
  145. for (EntityGroupItem item : items) {
  146. if (((INews) item.getEntity()).isVisible())
  147. elements.add(item.getEntity());
  148. }
  149. }
  150. }
  151. return elements.toArray();
  152. }
  153. /*
  154. * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
  155. */
  156. public Object[] getChildren(Object parentElement) {
  157. List<Object> children = new ArrayList<Object>();
  158. /* Handle EntityGroup */
  159. if (parentElement instanceof EntityGroup) {
  160. List<EntityGroupItem> items = ((EntityGroup) parentElement).getItems();
  161. for (EntityGroupItem item : items)
  162. children.add(item.getEntity());
  163. }
  164. return children.toArray();
  165. }
  166. /*
  167. * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
  168. */
  169. public Object getParent(Object element) {
  170. /* Handle Grouping specially */
  171. if (fGrouping.isActive() && element instanceof INews) {
  172. Collection<EntityGroup> groups = fGrouping.group(Collections.singletonList((INews) element));
  173. if (groups.size() == 1)
  174. return groups.iterator().next();
  175. }
  176. return null;
  177. }
  178. /*
  179. * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
  180. */
  181. public boolean hasChildren(Object element) {
  182. return element instanceof EntityGroup;
  183. }
  184. /*
  185. * @see org.eclipse.jface.viewers.IContentProvider#dispose()
  186. */
  187. public synchronized void dispose() {
  188. fDisposed = true;
  189. unregisterListeners();
  190. }
  191. /*
  192. * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
  193. * java.lang.Object, java.lang.Object)
  194. */
  195. public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
  196. /* Ignore - Input changes are handled via refreshCache(Object input) */
  197. }
  198. boolean isGroupingEnabled() {
  199. return fGrouping.getType() != NewsGrouping.Type.NO_GROUPING;
  200. }
  201. boolean isGroupingByFeed() {
  202. return fGrouping.getType() == NewsGrouping.Type.GROUP_BY_FEED;
  203. }
  204. boolean isGroupingByStickyness() {
  205. return fGrouping.getType() == NewsGrouping.Type.GROUP_BY_STICKY;
  206. }
  207. /* Returns the news that have been added since the last refresh */
  208. synchronized List<INews> refreshCache(INewsMark input, boolean onlyAdd) throws PersistenceException {
  209. List<INews> addedNews = Collections.emptyList();
  210. /* Update Input */
  211. fInput = input;
  212. /* Register Listeners if not yet done */
  213. if (fNewsListener == null)
  214. registerListeners();
  215. /* Clear old Data if required */
  216. if (fCachedNews == null)
  217. fCachedNews = new HashSet<INews>();
  218. else if (!onlyAdd)
  219. fCachedNews.clear();
  220. /* Check if ContentProvider was already disposed */
  221. if (fDisposed)
  222. return addedNews;
  223. /* Obtain the News */
  224. addedNews = new ArrayList<INews>();
  225. /* Special-case marks that can retrieve newsRefs cheaply */
  226. if (input.isGetNewsRefsEfficient()) {
  227. for (NewsReference newsRef : input.getNewsRefs(INews.State.getVisible())) {
  228. /* Avoid to resolve an already shown News */
  229. if (onlyAdd && hasCachedNews(newsRef))
  230. continue;
  231. /* Resolve and Add News */
  232. INews resolvedNews = newsRef.resolve();
  233. if (resolvedNews != null)
  234. addedNews.add(resolvedNews);
  235. }
  236. }
  237. else
  238. addedNews.addAll(input.getNews(INews.State.getVisible()));
  239. /* Add into Cache */
  240. fCachedNews.addAll(addedNews);
  241. return addedNews;
  242. }
  243. synchronized INewsMark getInput() {
  244. return fInput;
  245. }
  246. synchronized Collection<INews> getCachedNewsCopy() {
  247. if (fCachedNews == null)
  248. return null;
  249. return new ArrayList<INews>(fCachedNews);
  250. }
  251. synchronized boolean hasCachedNews() {
  252. return fCachedNews != null && !fCachedNews.isEmpty();
  253. }
  254. private synchronized boolean hasCachedNews(NewsReference ref) {
  255. if (fCachedNews == null)
  256. return false;
  257. for (INews news : fCachedNews) {
  258. if (ref.references(news))
  259. return true;
  260. }
  261. return false;
  262. }
  263. private synchronized boolean hasCachedNews(INews news) {
  264. if (fCachedNews == null)
  265. return false;
  266. for (INews cachedNews : fCachedNews) {
  267. if (cachedNews.equals(news))
  268. return true;
  269. }
  270. return false;
  271. }
  272. private synchronized INews obtainFromCache(NewsReference ref) {
  273. for (INews cachedNews : fCachedNews) {
  274. if (ref.references(cachedNews))
  275. return cachedNews;
  276. }
  277. return null;
  278. }
  279. private void registerListeners() {
  280. /* Saved Search Listener */
  281. fSearchMarkListener = new SearchMarkAdapter() {
  282. @Override
  283. public void resultsChanged(final Set<SearchMarkEvent> events) {
  284. for (SearchMarkEvent event : events) {
  285. ISearchMark searchMark = event.getEntity();
  286. if (fInput.equals(searchMark)) {
  287. JobRunner.runInUIThread(fFeedView.getEditorControl(), new Runnable() {
  288. public void run() {
  289. final boolean onlyHandleAddedNews = fFeedView.isVisible();
  290. JobRunner.runUIUpdater(new UIBackgroundJob(fFeedView.getEditorControl()) {
  291. private List<INews> fAddedNews;
  292. @Override
  293. protected void runInBackground(IProgressMonitor monitor) {
  294. if (!Controller.getDefault().isShuttingDown())
  295. fAddedNews = refreshCache(fInput, onlyHandleAddedNews);
  296. }
  297. @Override
  298. protected void runInUI(IProgressMonitor monitor) {
  299. if (Controller.getDefault().isShuttingDown())
  300. return;
  301. /* Check if we need to Refresh at all */
  302. if (onlyHandleAddedNews && (fAddedNews == null || fAddedNews.size() == 0))
  303. return;
  304. if (!browserShowsCollection())
  305. fFeedView.refreshTableViewer(true, true);
  306. else
  307. fFeedView.refresh(true, true); //TODO Seems some JFace caching problem here (redraw=true)
  308. }
  309. });
  310. }
  311. });
  312. /* Done */
  313. return;
  314. }
  315. }
  316. }
  317. };
  318. DynamicDAO.addEntityListener(ISearchMark.class, fSearchMarkListener);
  319. /* News Listener */
  320. fNewsListener = new NewsAdapter() {
  321. /* News got Added */
  322. @Override
  323. public void entitiesAdded(final Set<NewsEvent> events) {
  324. JobRunner.runInUIThread(fFeedView.getEditorControl(), new Runnable() {
  325. public void run() {
  326. Set<NewsEvent> addedNews = null;
  327. /* Filter News which are from a different Feed than displayed */
  328. for (NewsEvent event : events) {
  329. if (event.getEntity().isVisible() && isInputRelatedTo(event.getEntity(), EventType.PERSIST)) {
  330. if (addedNews == null)
  331. addedNews = new HashSet<NewsEvent>();
  332. addedNews.add(event);
  333. }
  334. /* Return on Shutdown */
  335. if (Controller.getDefault().isShuttingDown())
  336. return;
  337. }
  338. /* Event not interesting for us or we are disposed */
  339. if (addedNews == null || addedNews.size() == 0)
  340. return;
  341. /* Return on Shutdown */
  342. if (Controller.getDefault().isShuttingDown())
  343. return;
  344. /* Handle */
  345. boolean refresh = handleAddedNews(addedNews);
  346. if (refresh) {
  347. if (!browserShowsCollection())
  348. fFeedView.refreshTableViewer(true, false);
  349. else
  350. fFeedView.refresh(true, false);
  351. }
  352. }
  353. });
  354. }
  355. /* News got Updated */
  356. @Override
  357. public void entitiesUpdated(final Set<NewsEvent> events) {
  358. JobRunner.runInUIThread(fFeedView.getEditorControl(), new Runnable() {
  359. public void run() {
  360. Set<NewsEvent> restoredNews = null;
  361. Set<NewsEvent> updatedNews = null;
  362. Set<NewsEvent> deletedNews = null;
  363. /* Filter News which are from a different Feed than displayed */
  364. for (NewsEvent event : events) {
  365. /* Return on Shutdown */
  366. if (Controller.getDefault().isShuttingDown())
  367. return;
  368. /* Check if input relates to news events */
  369. if (isInputRelatedTo(event.getEntity(), EventType.UPDATE)) {
  370. INews news = event.getEntity();
  371. INews.State oldState = event.getOldNews().getState();
  372. /* News got Deleted */
  373. if (!news.isVisible()) {
  374. if (deletedNews == null)
  375. deletedNews = new HashSet<NewsEvent>();
  376. deletedNews.add(event);
  377. }
  378. /* News got Restored */
  379. else if (news.isVisible() && (oldState == INews.State.HIDDEN || oldState == INews.State.DELETED)) {
  380. if (restoredNews == null)
  381. restoredNews = new HashSet<NewsEvent>();
  382. restoredNews.add(event);
  383. }
  384. /* News got Updated */
  385. else {
  386. if (updatedNews == null)
  387. updatedNews = new HashSet<NewsEvent>();
  388. updatedNews.add(event);
  389. }
  390. }
  391. }
  392. boolean refresh = false;
  393. boolean updateSelectionFromDelete = false;
  394. /* Handle Restored News */
  395. if (restoredNews != null && !restoredNews.isEmpty())
  396. refresh = handleAddedNews(restoredNews);
  397. /* Handle Updated News */
  398. if (updatedNews != null && !updatedNews.isEmpty())
  399. refresh = handleUpdatedNews(updatedNews);
  400. /* Handle Deleted News */
  401. if (deletedNews != null && !deletedNews.isEmpty()) {
  402. refresh = handleDeletedNews(deletedNews);
  403. updateSelectionFromDelete = refresh;
  404. }
  405. /* Refresh and update selection due to deletion */
  406. if (updateSelectionFromDelete) {
  407. fTableViewer.updateSelectionAfterDelete(new Runnable() {
  408. public void run() {
  409. refreshViewers(events, EventType.REMOVE);
  410. }
  411. });
  412. }
  413. /* Normal refresh w/o deletion */
  414. else if (refresh)
  415. refreshViewers(events, EventType.UPDATE);
  416. }
  417. });
  418. }
  419. /* News got Deleted */
  420. @Override
  421. public void entitiesDeleted(final Set<NewsEvent> events) {
  422. JobRunner.runInUIThread(fFeedView.getEditorControl(), new Runnable() {
  423. public void run() {
  424. Set<NewsEvent> deletedNews = null;
  425. /* Filter News which are from a different Feed than displayed */
  426. for (NewsEvent event : events) {
  427. INews news = event.getEntity();
  428. if ((news.isVisible() || news.getParentId() != 0) && isInputRelatedTo(news, EventType.REMOVE)) {
  429. if (deletedNews == null)
  430. deletedNews = new HashSet<NewsEvent>();
  431. deletedNews.add(event);
  432. }
  433. }
  434. /* Return on Shutdown */
  435. if (Controller.getDefault().isShuttingDown())
  436. return;
  437. /* Event not interesting for us or we are disposed */
  438. if (deletedNews == null || deletedNews.size() == 0)
  439. return;
  440. /* Handle Deleted News */
  441. boolean refresh = handleDeletedNews(deletedNews);
  442. if (refresh) {
  443. if (!browserShowsCollection())
  444. fFeedView.refreshTableViewer(true, false);
  445. else
  446. fFeedView.refresh(true, false);
  447. }
  448. }
  449. });
  450. }
  451. };
  452. DynamicDAO.addEntityListener(INews.class, fNewsListener);
  453. }
  454. private void refreshViewers(final Set<NewsEvent> events, EventType type) {
  455. /* Return on Shutdown */
  456. if (Controller.getDefault().isShuttingDown())
  457. return;
  458. /*
  459. * Optimization: The Browser is likely only showing a single news and thus
  460. * there is no need to refresh the entire content but rather use the update
  461. * instead.
  462. */
  463. if (!browserShowsCollection()) {
  464. List<INews> items = new ArrayList<INews>(events.size());
  465. for (NewsEvent event : events) {
  466. items.add(event.getEntity());
  467. }
  468. /* Update Browser Viewer */
  469. if (contains(fBrowserViewer.getInput(), items)) {
  470. /* Update */
  471. if (type == EventType.UPDATE) {
  472. Set<NewsEvent> newsToUpdate = events;
  473. /*
  474. * Optimization: If more than a single news is to update, check
  475. * if the Browser only shows a single news to avoid a full refresh.
  476. */
  477. if (events.size() > 1) {
  478. NewsEvent event = findShowingEventFromBrowser(events);
  479. if (event != null)
  480. newsToUpdate = Collections.singleton(event);
  481. }
  482. fBrowserViewer.update(newsToUpdate);
  483. }
  484. /* Remove */
  485. else if (type == EventType.REMOVE)
  486. fBrowserViewer.remove(items.toArray());
  487. }
  488. /* Refresh Table Viewer */
  489. fFeedView.refreshTableViewer(true, true);
  490. }
  491. /* Browser is showing Collection, thereby perform a refresh */
  492. else
  493. fFeedView.refresh(true, true);
  494. }
  495. private boolean handleAddedNews(Set<NewsEvent> events) {
  496. /*
  497. * Input can be NULL if this listener was called before NewsTableControl.setPartInput()
  498. * has been called (can happen if the viewer has thousands of items to load)
  499. */
  500. if (fFeedView.isTableViewerVisible() && fTableViewer.getInput() == null)
  501. return false;
  502. /* Receive added News */
  503. List<INews> addedNews = new ArrayList<INews>(events.size());
  504. for (NewsEvent event : events) {
  505. addedNews.add(event.getEntity());
  506. }
  507. /* Add to Cache */
  508. synchronized (NewsContentProvider.this) {
  509. fCachedNews.addAll(addedNews);
  510. }
  511. /* Return early if a refresh is required anyways */
  512. if (fGrouping.needsRefresh(INews.class, events, false))
  513. return true;
  514. addToViewers(addedNews);
  515. return false;
  516. }
  517. /* Add a List of News to Table and Browser Viewers */
  518. private void addToViewers(List<INews> addedNews) {
  519. /* Add to Table-Viewer if Visible (keep top item and selection stable) */
  520. if (fFeedView.isTableViewerVisible()) {
  521. Tree tree = fTableViewer.getTree();
  522. TreeItem topItem = tree.getTopItem();
  523. int indexOfTopItem = 0;
  524. if (topItem != null)
  525. indexOfTopItem = tree.indexOf(topItem);
  526. tree.setRedraw(false);
  527. try {
  528. fTableViewer.add(fTableViewer.getInput(), addedNews.toArray());
  529. if (topItem != null && indexOfTopItem != 0)
  530. tree.setTopItem(topItem);
  531. } finally {
  532. tree.setRedraw(true);
  533. }
  534. }
  535. /* Add to Browser-Viewer if showing entire Feed */
  536. if (browserShowsCollection())
  537. fBrowserViewer.add(fBrowserViewer.getInput(), addedNews.toArray());
  538. }
  539. /* Browser shows collection if maximized */
  540. private boolean browserShowsCollection() {
  541. Object input = fBrowserViewer.getInput();
  542. return (input instanceof BookMarkReference || input instanceof NewsBinReference || input instanceof SearchMarkReference || input instanceof FolderNewsMarkReference);
  543. }
  544. private boolean handleUpdatedNews(Set<NewsEvent> events) {
  545. /* Receive updated News */
  546. List<INews> updatedNews = new ArrayList<INews>(events.size());
  547. for (NewsEvent event : events) {
  548. updatedNews.add(event.getEntity());
  549. }
  550. /* Return early if refresh is required anyways for Grouper */
  551. if (fGrouping.needsRefresh(INews.class, events, true))
  552. return true;
  553. /* Return early if refresh is required anyways for Filter */
  554. if (fFilter.needsRefresh(events))
  555. return true;
  556. /* Update in Table-Viewer */
  557. if (fFeedView.isTableViewerVisible())
  558. fTableViewer.update(updatedNews.toArray(), null);
  559. /* Update in Browser-Viewer */
  560. if (contains(fBrowserViewer.getInput(), updatedNews)) {
  561. Set<NewsEvent> newsToUpdate = events;
  562. /*
  563. * Optimization: If more than a single news is to update, check
  564. * if the Browser only shows a single news to avoid a full refresh.
  565. */
  566. if (events.size() > 1) {
  567. NewsEvent event = findShowingEventFromBrowser(events);
  568. if (event != null)
  569. newsToUpdate = Collections.singleton(event);
  570. }
  571. fBrowserViewer.update(newsToUpdate);
  572. }
  573. return false;
  574. }
  575. private boolean handleDeletedNews(Set<NewsEvent> events) {
  576. /* Receive deleted News */
  577. List<INews> deletedNews = new ArrayList<INews>(events.size());
  578. for (NewsEvent event : events) {
  579. deletedNews.add(event.getEntity());
  580. }
  581. /* Remove from Cache */
  582. synchronized (NewsContentProvider.this) {
  583. fCachedNews.removeAll(deletedNews);
  584. }
  585. /* Return early if refresh is required anyways */
  586. if (fGrouping.needsRefresh(INews.class, events, false))
  587. return true;
  588. /* Grouping Disabled */
  589. if (!isGroupingEnabled()) {
  590. /* Remove from Table-Viewer */
  591. if (fFeedView.isTableViewerVisible())
  592. fTableViewer.remove(deletedNews.toArray());
  593. /* Remove from Browser-Viewer */
  594. if (contains(fBrowserViewer.getInput(), deletedNews))
  595. fBrowserViewer.remove(deletedNews.toArray());
  596. }
  597. return false;
  598. }
  599. private void unregisterListeners() {
  600. DynamicDAO.removeEntityListener(INews.class, fNewsListener);
  601. DynamicDAO.removeEntityListener(ISearchMark.class, fSearchMarkListener);
  602. }
  603. private boolean isInputRelatedTo(INews news, EventType type) {
  604. /* Check if BookMark references the News' Feed and is not a copy */
  605. if (fInput instanceof IBookMark) {
  606. IBookMark bookmark = (IBookMark) fInput;
  607. if (news.getParentId() == 0 && bookmark.getFeedLinkReference().equals(news.getFeedReference()))
  608. return true;
  609. }
  610. /* Check if Saved Search contains the given News */
  611. else if (type != EventType.PERSIST && fInput instanceof ISearchMark) {
  612. /*
  613. * Workaround a race condition in a safe way: When a News gets updated or deleted from a
  614. * Searchmark, the Indexer is the first to process this event. Since the SavedSearchService
  615. * updates all Searchmarks instantly as a result of that, the Searchmark at this point could no
  616. * longer contain the affected News and isInputRelated() would return false. The fix is
  617. * to check the cache for the News instead of the potential modified Searchmark.
  618. */
  619. return hasCachedNews(news);
  620. }
  621. /* Update / Remove: Check if News points to this Bin */
  622. else if (fInput instanceof INewsBin) {
  623. return news.getParentId() == fInput.getId();
  624. }
  625. /* In Memory Folder News Mark (aggregated news) */
  626. else if (fInput instanceof FolderNewsMark) {
  627. /* News Added/Updated: Check if its part of the Folder */
  628. if (type == EventType.PERSIST || type == EventType.UPDATE) {
  629. return ((FolderNewsMark) fInput).isRelatedTo(news);
  630. }
  631. /* Remove: Check if news was cached */
  632. return hasCachedNews(news);
  633. }
  634. return false;
  635. }
  636. private boolean contains(Object input, List<INews> list) {
  637. /* Can only belong to this Feed since filtered before already */
  638. if (input instanceof BookMarkReference || input instanceof NewsBinReference || input instanceof SearchMarkReference || input instanceof FolderNewsMarkReference)
  639. return true;
  640. else if (input instanceof INews)
  641. return list.contains(input);
  642. else if (input instanceof EntityGroup) {
  643. List<EntityGroupItem> items = ((EntityGroup) input).getItems();
  644. for (EntityGroupItem item : items) {
  645. if (list.contains(item.getEntity()))
  646. return true;
  647. }
  648. }
  649. else if (input instanceof Object[]) {
  650. Object inputNews[] = (Object[]) input;
  651. for (Object inputNewsItem : inputNews) {
  652. if (list.contains(inputNewsItem))
  653. return true;
  654. }
  655. }
  656. return false;
  657. }
  658. private NewsEvent findShowingEventFromBrowser(Set<NewsEvent> events) {
  659. Object input = fBrowserViewer.getInput();
  660. if (input instanceof INews) {
  661. INews news = (INews) input;
  662. for (NewsEvent event : events) {
  663. if (news.equals(event.getEntity()))
  664. return event;
  665. }
  666. }
  667. return null;
  668. }
  669. }