PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/rssowl-2.0.5/org.rssowl.ui/src/org/rssowl/ui/internal/views/explorer/BookMarkContentProvider.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 829 lines | 529 code | 148 blank | 152 comment | 173 complexity | 6599c1cb1afbbbbb6b636fc2c102ef1f 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.views.explorer;
  25. import org.eclipse.core.runtime.Assert;
  26. import org.eclipse.jface.viewers.ISelection;
  27. import org.eclipse.jface.viewers.ITreeContentProvider;
  28. import org.eclipse.jface.viewers.StructuredSelection;
  29. import org.eclipse.jface.viewers.TreeViewer;
  30. import org.eclipse.jface.viewers.Viewer;
  31. import org.rssowl.core.persist.IBookMark;
  32. import org.rssowl.core.persist.IEntity;
  33. import org.rssowl.core.persist.IFolder;
  34. import org.rssowl.core.persist.IFolderChild;
  35. import org.rssowl.core.persist.IMark;
  36. import org.rssowl.core.persist.INews;
  37. import org.rssowl.core.persist.INewsBin;
  38. import org.rssowl.core.persist.ISearchMark;
  39. import org.rssowl.core.persist.dao.DynamicDAO;
  40. import org.rssowl.core.persist.dao.IBookMarkDAO;
  41. import org.rssowl.core.persist.dao.INewsBinDAO;
  42. import org.rssowl.core.persist.event.BookMarkEvent;
  43. import org.rssowl.core.persist.event.BookMarkListener;
  44. import org.rssowl.core.persist.event.FolderEvent;
  45. import org.rssowl.core.persist.event.FolderListener;
  46. import org.rssowl.core.persist.event.MarkEvent;
  47. import org.rssowl.core.persist.event.NewsAdapter;
  48. import org.rssowl.core.persist.event.NewsBinEvent;
  49. import org.rssowl.core.persist.event.NewsBinListener;
  50. import org.rssowl.core.persist.event.NewsEvent;
  51. import org.rssowl.core.persist.event.NewsListener;
  52. import org.rssowl.core.persist.event.SearchMarkEvent;
  53. import org.rssowl.core.persist.event.SearchMarkListener;
  54. import org.rssowl.core.persist.reference.FeedLinkReference;
  55. import org.rssowl.core.persist.service.PersistenceException;
  56. import org.rssowl.core.util.CoreUtils;
  57. import org.rssowl.ui.internal.Controller;
  58. import org.rssowl.ui.internal.EntityGroup;
  59. import org.rssowl.ui.internal.util.JobRunner;
  60. import java.util.ArrayList;
  61. import java.util.Arrays;
  62. import java.util.Collection;
  63. import java.util.Collections;
  64. import java.util.HashMap;
  65. import java.util.HashSet;
  66. import java.util.List;
  67. import java.util.Map;
  68. import java.util.Set;
  69. import java.util.Map.Entry;
  70. /**
  71. * @author bpasero
  72. */
  73. public class BookMarkContentProvider implements ITreeContentProvider {
  74. /* Delay in ms before updating Selection on Events */
  75. private static final int SELECTION_DELAY = 20;
  76. /* Listener */
  77. private FolderListener fFolderListener;
  78. private BookMarkListener fBookMarkListener;
  79. private NewsBinListener fNewsBinListener;
  80. private SearchMarkListener fSearchMarkListener;
  81. private NewsListener fNewsListener;
  82. /* Viewer Related */
  83. private IFolder fInput;
  84. private TreeViewer fViewer;
  85. private BookMarkFilter fBookmarkFilter;
  86. private BookMarkGrouping fBookmarkGrouping;
  87. /* Misc. */
  88. private IBookMarkDAO fBookMarkDAO = DynamicDAO.getDAO(IBookMarkDAO.class);
  89. /*
  90. * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
  91. */
  92. public Object[] getElements(Object inputElement) {
  93. if (inputElement instanceof IFolder) {
  94. IFolder rootFolder = (IFolder) inputElement;
  95. /* No Grouping */
  96. if (!fBookmarkGrouping.isActive()) {
  97. Collection<IFolderChild> elements = rootFolder.getChildren();
  98. /* Return Children */
  99. return elements.toArray();
  100. }
  101. /* Grouping Enabled */
  102. List<IMark> marks = new ArrayList<IMark>();
  103. getAllMarks(rootFolder, marks);
  104. return fBookmarkGrouping.group(marks);
  105. }
  106. return new Object[0];
  107. }
  108. /*
  109. * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
  110. */
  111. public Object[] getChildren(Object parentElement) {
  112. /* Return Children of Folder */
  113. if (parentElement instanceof IFolder) {
  114. IFolder parent = (IFolder) parentElement;
  115. Collection<IFolderChild> children = parent.getChildren();
  116. return children.toArray();
  117. }
  118. /* Return Children of Group */
  119. else if (parentElement instanceof EntityGroup) {
  120. List<IEntity> children = ((EntityGroup) parentElement).getEntities();
  121. return children.toArray();
  122. }
  123. return new Object[0];
  124. }
  125. /*
  126. * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
  127. */
  128. public Object getParent(Object element) {
  129. /* Handle Grouping specially */
  130. if (fBookmarkGrouping.isActive() && element instanceof IEntity) {
  131. IEntity entity = (IEntity) element;
  132. EntityGroup[] groups = fBookmarkGrouping.group(Collections.singletonList(entity));
  133. if (groups.length == 1)
  134. return groups[0];
  135. }
  136. /* Grouping not enabled */
  137. else {
  138. /* Parent Folder of Folder */
  139. if (element instanceof IFolder) {
  140. IFolder folder = (IFolder) element;
  141. return folder.getParent();
  142. }
  143. /* Parent Folder of Mark */
  144. else if (element instanceof IMark) {
  145. IMark mark = (IMark) element;
  146. return mark.getParent();
  147. }
  148. }
  149. return null;
  150. }
  151. /*
  152. * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
  153. */
  154. public boolean hasChildren(Object element) {
  155. if (element instanceof IFolder) {
  156. IFolder folder = (IFolder) element;
  157. return folder.getChildren().size() > 0;
  158. }
  159. else if (element instanceof EntityGroup)
  160. return ((EntityGroup) element).size() > 0;
  161. return false;
  162. }
  163. /*
  164. * @see org.eclipse.jface.viewers.IContentProvider#dispose()
  165. */
  166. public void dispose() {
  167. unregisterListeners();
  168. }
  169. /*
  170. * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
  171. * java.lang.Object, java.lang.Object)
  172. */
  173. public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
  174. Assert.isTrue(newInput instanceof IFolder || newInput == null);
  175. fViewer = (TreeViewer) viewer;
  176. fInput = (IFolder) newInput;
  177. /* Register Listeners if Input is available */
  178. if (newInput != null && oldInput == null)
  179. registerListeners();
  180. /* If new Input is NULL, unregister Listeners */
  181. else if (newInput == null && oldInput != null)
  182. unregisterListeners();
  183. }
  184. /* The ContentProvider needs to know about this Filter */
  185. void setBookmarkFilter(BookMarkFilter bookmarkFilter) {
  186. fBookmarkFilter = bookmarkFilter;
  187. }
  188. /* The ContentProvider needs to know about this Grouping */
  189. void setBookmarkGrouping(BookMarkGrouping bookmarkGrouping) {
  190. fBookmarkGrouping = bookmarkGrouping;
  191. }
  192. private void registerListeners() {
  193. /* Folder Listener */
  194. fFolderListener = new FolderListener() {
  195. /* Folders got updated */
  196. public void entitiesUpdated(final Set<FolderEvent> events) {
  197. JobRunner.runInUIThread(fViewer.getControl(), new Runnable() {
  198. public void run() {
  199. Set<IFolder> updatedFolders = null;
  200. Map<IFolder, IFolder> reparentedFolders = null;
  201. /* Retrieve Updated Folders */
  202. for (FolderEvent event : events) {
  203. if (event.isRoot()) {
  204. /* Folder got reparented */
  205. if (event.getOldParent() != null) {
  206. if (reparentedFolders == null)
  207. reparentedFolders = new HashMap<IFolder, IFolder>();
  208. reparentedFolders.put(event.getEntity(), event.getOldParent());
  209. }
  210. /* Normal Update */
  211. else {
  212. if (updatedFolders == null)
  213. updatedFolders = new HashSet<IFolder>();
  214. updatedFolders.add(event.getEntity());
  215. }
  216. }
  217. }
  218. /* Event not interesting for us or we are disposed */
  219. if (updatedFolders == null && reparentedFolders == null)
  220. return;
  221. /* Ask Filter */
  222. if (fBookmarkFilter.needsRefresh(IFolder.class, events))
  223. fViewer.refresh(false);
  224. /* Ask Group */
  225. else if (fBookmarkGrouping.needsRefresh(IFolder.class))
  226. fViewer.refresh(false);
  227. /* Handle reparented Folders */
  228. else if (reparentedFolders != null) {
  229. Set<Entry<IFolder, IFolder>> entries = reparentedFolders.entrySet();
  230. Set<IFolder> parentsToUpdate = new HashSet<IFolder>();
  231. List<Object> expandedElements = new ArrayList<Object>(Arrays.asList(fViewer.getExpandedElements()));
  232. try {
  233. fViewer.getControl().getParent().setRedraw(false);
  234. for (Entry<IFolder, IFolder> entry : entries) {
  235. IFolder reparentedFolder = entry.getKey();
  236. IFolder oldParent = entry.getValue();
  237. /* Reparent while keeping the Selection / Expansion */
  238. ISelection selection = fViewer.getSelection();
  239. boolean expand = expandedElements.contains(reparentedFolder);
  240. fViewer.remove(oldParent, new Object[] { reparentedFolder });
  241. fViewer.refresh(reparentedFolder.getParent(), false);
  242. fViewer.setSelection(selection);
  243. if (expand)
  244. fViewer.setExpandedState(reparentedFolder, expand);
  245. /* Remember to update parents */
  246. parentsToUpdate.add(oldParent);
  247. parentsToUpdate.add(reparentedFolder.getParent());
  248. }
  249. } finally {
  250. fViewer.getControl().getParent().setRedraw(true);
  251. }
  252. /* Update old Parents of Reparented Bookmarks */
  253. for (IFolder folder : parentsToUpdate)
  254. updateFolderAndParents(folder);
  255. }
  256. /* Handle Updated Folders */
  257. if (updatedFolders != null) {
  258. for (IFolder folder : updatedFolders) {
  259. if (fInput.equals(folder))
  260. fViewer.refresh(fInput);
  261. else
  262. fViewer.refresh(folder);
  263. }
  264. }
  265. }
  266. });
  267. }
  268. /* Folders got deleted */
  269. public void entitiesDeleted(final Set<FolderEvent> events) {
  270. JobRunner.runInUIThread(fViewer.getControl(), new Runnable() {
  271. public void run() {
  272. /* Retrieve Removed Folders */
  273. Set<IFolder> removedFolders = null;
  274. for (FolderEvent event : events) {
  275. if (event.isRoot() && event.getEntity().getParent() != null) {
  276. if (removedFolders == null)
  277. removedFolders = new HashSet<IFolder>();
  278. removedFolders.add(event.getEntity());
  279. }
  280. }
  281. /* Event not interesting for us or we are disposed */
  282. if (removedFolders == null || removedFolders.size() == 0)
  283. return;
  284. /* Ask Filter */
  285. if (fBookmarkFilter.needsRefresh(IFolder.class, events))
  286. fViewer.refresh(false);
  287. /* Ask Group */
  288. else if (fBookmarkGrouping.needsRefresh(IFolder.class))
  289. fViewer.refresh(false);
  290. /* React normally then */
  291. else
  292. fViewer.remove(removedFolders.toArray());
  293. /* Update Read-State counters on Parents */
  294. if (!fBookmarkGrouping.isActive()) {
  295. for (FolderEvent event : events) {
  296. IFolder eventParent = event.getEntity().getParent();
  297. if (eventParent != null && eventParent.getParent() != null)
  298. updateFolderAndParents(eventParent);
  299. }
  300. }
  301. }
  302. });
  303. }
  304. /* Folders got added */
  305. public void entitiesAdded(final Set<FolderEvent> events) {
  306. JobRunner.runInUIThread(SELECTION_DELAY, fViewer.getControl(), new Runnable() {
  307. public void run() {
  308. /* Reveal and Select added Folders */
  309. final List<IFolder> addedFolders = new ArrayList<IFolder>();
  310. for (FolderEvent folderEvent : events) {
  311. IFolder addedFolder = folderEvent.getEntity();
  312. if (addedFolder.getParent() != null)
  313. addedFolders.add(addedFolder);
  314. }
  315. if (addedFolders.size() == 1)
  316. fViewer.setSelection(new StructuredSelection(addedFolders), true);
  317. }
  318. });
  319. }
  320. };
  321. /* BookMark Listener */
  322. fBookMarkListener = new BookMarkListener() {
  323. /* BookMarks got Updated */
  324. public void entitiesUpdated(final Set<BookMarkEvent> events) {
  325. onMarksUpdated(events);
  326. }
  327. /* BookMarks got Deleted */
  328. public void entitiesDeleted(final Set<BookMarkEvent> events) {
  329. onMarksRemoved(events);
  330. }
  331. /* BookMarks got Added */
  332. public void entitiesAdded(Set<BookMarkEvent> events) {
  333. onMarksAdded(events);
  334. }
  335. };
  336. /* SearchMark Listener */
  337. fSearchMarkListener = new SearchMarkListener() {
  338. /* SearchMarks got Updated */
  339. public void entitiesUpdated(final Set<SearchMarkEvent> events) {
  340. onMarksUpdated(events);
  341. }
  342. /* SearchMarks got Deleted */
  343. public void entitiesDeleted(final Set<SearchMarkEvent> events) {
  344. onMarksRemoved(events);
  345. }
  346. /* SearchMarks got Added */
  347. public void entitiesAdded(Set<SearchMarkEvent> events) {
  348. onMarksAdded(events);
  349. }
  350. /* SearchMark result changed */
  351. public void resultsChanged(final Set<SearchMarkEvent> events) {
  352. JobRunner.runInUIThread(fViewer.getControl(), new Runnable() {
  353. public void run() {
  354. /* Ask Filter for a refresh */
  355. if (fBookmarkFilter.needsRefresh(ISearchMark.class, events, true))
  356. fViewer.refresh(false);
  357. /* Update SearchMarks */
  358. Set<ISearchMark> updatedSearchMarks = new HashSet<ISearchMark>(events.size());
  359. for (SearchMarkEvent event : events) {
  360. updatedSearchMarks.add(event.getEntity());
  361. }
  362. fViewer.update(updatedSearchMarks.toArray(), null);
  363. /* Update Parents */
  364. for (ISearchMark searchMark : updatedSearchMarks)
  365. updateFolderAndParents(searchMark.getParent());
  366. }
  367. });
  368. }
  369. };
  370. /* NewsBin Listener */
  371. fNewsBinListener = new NewsBinListener() {
  372. /* NewsBins got Updated */
  373. public void entitiesUpdated(final Set<NewsBinEvent> events) {
  374. onMarksUpdated(events);
  375. }
  376. /* NewsBins got Deleted */
  377. public void entitiesDeleted(final Set<NewsBinEvent> events) {
  378. onMarksRemoved(events);
  379. }
  380. /* Newsbins got Added */
  381. public void entitiesAdded(Set<NewsBinEvent> events) {
  382. onMarksAdded(events);
  383. }
  384. };
  385. /* News Listener */
  386. fNewsListener = new NewsAdapter() {
  387. @Override
  388. public void entitiesAdded(final Set<NewsEvent> events) {
  389. JobRunner.runInUIThread(fViewer.getControl(), new Runnable() {
  390. public void run() {
  391. /* Return on Shutdown */
  392. if (Controller.getDefault().isShuttingDown())
  393. return;
  394. /* Ask Filter */
  395. if (fBookmarkFilter.needsRefresh(INews.class, events))
  396. fViewer.refresh(false);
  397. /* Ask Group */
  398. else if (fBookmarkGrouping.needsRefresh(INews.class))
  399. fViewer.refresh(false);
  400. /* Updated affected Types on read-state if required */
  401. if (requiresUpdate(events))
  402. updateParents(events);
  403. }
  404. });
  405. }
  406. @Override
  407. public void entitiesUpdated(final Set<NewsEvent> events) {
  408. JobRunner.runInUIThread(fViewer.getControl(), new Runnable() {
  409. public void run() {
  410. /* Return on Shutdown */
  411. if (Controller.getDefault().isShuttingDown())
  412. return;
  413. /* Ask Filter */
  414. if (fBookmarkFilter.needsRefresh(INews.class, events))
  415. fViewer.refresh(false);
  416. /* Ask Group */
  417. else if (fBookmarkGrouping.needsRefresh(INews.class))
  418. fViewer.refresh(false);
  419. /* Updated affected Types on read-state if required */
  420. if (requiresUpdate(events))
  421. updateParents(events);
  422. }
  423. });
  424. }
  425. };
  426. /* Register Listeners */
  427. DynamicDAO.addEntityListener(IFolder.class, fFolderListener);
  428. DynamicDAO.addEntityListener(IBookMark.class, fBookMarkListener);
  429. DynamicDAO.addEntityListener(INewsBin.class, fNewsBinListener);
  430. DynamicDAO.addEntityListener(ISearchMark.class, fSearchMarkListener);
  431. DynamicDAO.addEntityListener(INews.class, fNewsListener);
  432. }
  433. private void onMarksAdded(Set<? extends MarkEvent> events) {
  434. /* Reveal and Select if single Entity added */
  435. if (events.size() == 1) {
  436. final MarkEvent event = events.iterator().next();
  437. JobRunner.runInUIThread(fViewer.getControl(), new Runnable() {
  438. public void run() {
  439. expand(event.getEntity().getParent());
  440. }
  441. });
  442. }
  443. }
  444. private void onMarksRemoved(final Set<? extends MarkEvent> events) {
  445. if (events.isEmpty())
  446. return;
  447. JobRunner.runInUIThread(fViewer.getControl(), new Runnable() {
  448. public void run() {
  449. /* Retrieve Removed Marks */
  450. Class<? extends IMark> clazz = null;
  451. Set<IMark> removedMarks = null;
  452. for (MarkEvent event : events) {
  453. if (event.isRoot()) {
  454. if (removedMarks == null)
  455. removedMarks = new HashSet<IMark>();
  456. removedMarks.add(event.getEntity());
  457. }
  458. if (clazz == null)
  459. clazz = event.getEntity().getClass();
  460. }
  461. /* Event not interesting for us or we are disposed */
  462. if (removedMarks == null || removedMarks.size() == 0)
  463. return;
  464. /* Ask Filter */
  465. if (fBookmarkFilter.needsRefresh(clazz, events))
  466. fViewer.refresh(false);
  467. /* Ask Group */
  468. else if (fBookmarkGrouping.needsRefresh(clazz))
  469. fViewer.refresh(false);
  470. /* React normally then */
  471. else
  472. fViewer.remove(removedMarks.toArray());
  473. /* Update Read-State counters on Parents */
  474. if (!fBookmarkGrouping.isActive()) {
  475. for (MarkEvent event : events) {
  476. IFolder eventParent = event.getEntity().getParent();
  477. if (eventParent != null && eventParent.getParent() != null)
  478. updateFolderAndParents(eventParent);
  479. }
  480. }
  481. }
  482. });
  483. }
  484. private void onMarksUpdated(final Set<? extends MarkEvent> events) {
  485. if (events.isEmpty())
  486. return;
  487. JobRunner.runInUIThread(fViewer.getControl(), new Runnable() {
  488. public void run() {
  489. Class<? extends IMark> clazz = null;
  490. Set<IMark> updatedMarks = null;
  491. Map<IMark, IFolder> reparentedMarks = null;
  492. /* Retrieve Updated Marks */
  493. for (MarkEvent event : events) {
  494. if (event.isRoot()) {
  495. IFolder oldParent = event.getOldParent();
  496. /* Mark got reparented */
  497. if (oldParent != null) {
  498. if (reparentedMarks == null)
  499. reparentedMarks = new HashMap<IMark, IFolder>();
  500. reparentedMarks.put(event.getEntity(), oldParent);
  501. }
  502. /* Normal Update */
  503. else {
  504. if (updatedMarks == null)
  505. updatedMarks = new HashSet<IMark>();
  506. updatedMarks.add(event.getEntity());
  507. }
  508. }
  509. if (clazz == null)
  510. clazz = event.getEntity().getClass();
  511. }
  512. /* Event not interesting for us or we are disposed */
  513. if (updatedMarks == null && reparentedMarks == null)
  514. return;
  515. /* Ask Filter */
  516. if (fBookmarkFilter.needsRefresh(clazz, events))
  517. fViewer.refresh(false);
  518. /* Ask Group */
  519. else if (fBookmarkGrouping.needsRefresh(clazz))
  520. fViewer.refresh(false);
  521. /* Handle reparented Marks */
  522. else if (reparentedMarks != null) {
  523. Set<Entry<IMark, IFolder>> entries = reparentedMarks.entrySet();
  524. Set<IFolder> parentsToUpdate = new HashSet<IFolder>();
  525. try {
  526. fViewer.getControl().getParent().setRedraw(false);
  527. for (Entry<IMark, IFolder> entry : entries) {
  528. IMark reparentedMark = entry.getKey();
  529. IFolder oldParent = entry.getValue();
  530. /* Reparent while keeping the Selection */
  531. ISelection selection = fViewer.getSelection();
  532. fViewer.remove(oldParent, new Object[] { reparentedMark });
  533. fViewer.refresh(reparentedMark.getParent(), false);
  534. fViewer.setSelection(selection);
  535. /* Remember to update parents */
  536. parentsToUpdate.add(oldParent);
  537. parentsToUpdate.add(reparentedMark.getParent());
  538. }
  539. } finally {
  540. fViewer.getControl().getParent().setRedraw(true);
  541. }
  542. /* Update old Parents of Reparented Marks */
  543. for (IFolder folder : parentsToUpdate)
  544. updateFolderAndParents(folder);
  545. }
  546. /* Handle Updated Marks */
  547. if (updatedMarks != null)
  548. fViewer.update(updatedMarks.toArray(), null);
  549. }
  550. });
  551. }
  552. private void unregisterListeners() {
  553. DynamicDAO.removeEntityListener(IFolder.class, fFolderListener);
  554. DynamicDAO.removeEntityListener(IBookMark.class, fBookMarkListener);
  555. DynamicDAO.removeEntityListener(INewsBin.class, fNewsBinListener);
  556. DynamicDAO.removeEntityListener(ISearchMark.class, fSearchMarkListener);
  557. DynamicDAO.removeEntityListener(INews.class, fNewsListener);
  558. }
  559. /* Update Entities that are affected by the given NewsEvents */
  560. private void updateParents(final Set<NewsEvent> events) {
  561. INewsBinDAO newsBinDao = DynamicDAO.getDAO(INewsBinDAO.class);
  562. /* Group by Feed and Bins */
  563. Set<FeedLinkReference> affectedFeeds = new HashSet<FeedLinkReference>();
  564. Set<IFolder> affectedBinFolders = new HashSet<IFolder>();
  565. Set<Long> handledBins = new HashSet<Long>();
  566. for (NewsEvent event : events) {
  567. INews news = event.getEntity();
  568. long parentId = news.getParentId();
  569. if (parentId != 0) {
  570. if (!handledBins.contains(parentId)) {
  571. INewsBin bin = newsBinDao.load(parentId);
  572. if (bin != null) //Could have been deleted meanwhile
  573. affectedBinFolders.add(bin.getParent());
  574. handledBins.add(parentId);
  575. }
  576. } else
  577. affectedFeeds.add(news.getFeedReference());
  578. }
  579. /* Return on Shutdown */
  580. if (Controller.getDefault().isShuttingDown())
  581. return;
  582. /* Update related Entities */
  583. for (FeedLinkReference feedRef : affectedFeeds)
  584. updateParents(feedRef);
  585. for (IFolder folder : affectedBinFolders)
  586. updateFolderAndParents(folder);
  587. }
  588. private void updateParents(FeedLinkReference feedRef) throws PersistenceException {
  589. /* Collect all affected BookMarks */
  590. Collection<IBookMark> affectedBookMarks = fBookMarkDAO.loadAll(feedRef);
  591. /* Return on Shutdown */
  592. if (Controller.getDefault().isShuttingDown())
  593. return;
  594. /* Update them including Parents */
  595. updateMarksAndParents(affectedBookMarks);
  596. }
  597. private void updateMarksAndParents(Collection<IBookMark> bookmarks) {
  598. Set<IEntity> entitiesToUpdate = new HashSet<IEntity>();
  599. entitiesToUpdate.addAll(bookmarks);
  600. /* Collect parents */
  601. for (IBookMark bookmark : bookmarks) {
  602. List<IFolder> visibleParents = new ArrayList<IFolder>();
  603. collectParents(visibleParents, bookmark);
  604. entitiesToUpdate.addAll(visibleParents);
  605. /* Return on Shutdown */
  606. if (Controller.getDefault().isShuttingDown())
  607. return;
  608. }
  609. /* Update Entities */
  610. fViewer.update(entitiesToUpdate.toArray(), null);
  611. }
  612. private void collectParents(List<IFolder> parents, IEntity entity) {
  613. /* Determine Parent Folder */
  614. IFolder parent = null;
  615. if (entity instanceof IMark)
  616. parent = ((IMark) entity).getParent();
  617. else if (entity instanceof IFolder)
  618. parent = ((IFolder) entity).getParent();
  619. /* Root reached */
  620. if (parent == null)
  621. return;
  622. /* Input reached */
  623. if (fInput.equals(parent))
  624. return;
  625. /* Check parent visible */
  626. parents.add(parent);
  627. /* Recursively collect visible parents */
  628. collectParents(parents, parent);
  629. }
  630. private void updateFolderAndParents(IFolder folder) {
  631. Set<IEntity> entitiesToUpdate = new HashSet<IEntity>();
  632. entitiesToUpdate.add(folder);
  633. /* Collect parents */
  634. List<IFolder> parents = new ArrayList<IFolder>();
  635. collectParents(parents, folder);
  636. entitiesToUpdate.addAll(parents);
  637. /* Return on Shutdown */
  638. if (Controller.getDefault().isShuttingDown())
  639. return;
  640. /* Update Entities */
  641. fViewer.update(entitiesToUpdate.toArray(), null);
  642. }
  643. private void getAllMarks(IFolder folder, List<IMark> marks) {
  644. /* Add all Marks */
  645. marks.addAll(folder.getMarks());
  646. /* Go through Subfolders */
  647. List<IFolder> folders = folder.getFolders();
  648. for (IFolder childFolder : folders)
  649. getAllMarks(childFolder, marks);
  650. }
  651. private boolean requiresUpdate(Set<NewsEvent> events) {
  652. for (NewsEvent newsEvent : events) {
  653. INews oldNews = newsEvent.getOldNews();
  654. INews currentNews = newsEvent.getEntity();
  655. /* Check Change in New-State */
  656. boolean oldStateNew = INews.State.NEW.equals(oldNews != null ? oldNews.getState() : null);
  657. boolean currentStateNew = INews.State.NEW.equals(currentNews.getState());
  658. if (oldStateNew != currentStateNew)
  659. return true;
  660. /* Check Change in Read-State */
  661. boolean oldStateUnread = CoreUtils.isUnread(oldNews != null ? oldNews.getState() : null);
  662. boolean newStateUnread = CoreUtils.isUnread(currentNews.getState());
  663. if (oldStateUnread != newStateUnread)
  664. return true;
  665. /* Check Change in Sticky-State */
  666. boolean oldStateSticky = oldNews != null ? oldNews.isFlagged() : false;
  667. boolean newStateSticky = currentNews.isVisible() && currentNews.isFlagged();
  668. if (oldStateSticky != newStateSticky)
  669. return true;
  670. }
  671. return false;
  672. }
  673. /* Recursively expand a folder and all parents */
  674. private void expand(IFolder folder) {
  675. IFolder parent = folder.getParent();
  676. if (parent != null)
  677. expand(parent);
  678. if (folder.getParent() != null) //Never expand Set, its visible anyways
  679. fViewer.setExpandedState(folder, true);
  680. }
  681. }