| /******************************************************************************* |
| * Copyright (c) 2015 IBM Corporation and others. |
| * 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.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| ******************************************************************************/ |
| |
| package org.eclipse.ui.internal.views.markers; |
| |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.ui.views.markers.internal.MarkerMessages; |
| |
| /** |
| * The job that performs incremental update. Once the processing is complete it |
| * schedules an UI update. If it'll be possible and beneficial switch to |
| * incremental updatation, this has been left out for further investigation(*). |
| * only, and not used currently. |
| * |
| * @since 3.6 |
| * |
| */ |
| class IncrementUpdateJob extends MarkerUpdateJob { |
| |
| private LinkedList<MarkerEntry> incrementEntryList; |
| private LinkedList<MarkerUpdate> updateQueue; |
| |
| /** |
| * @param builder |
| */ |
| public IncrementUpdateJob(CachedMarkerBuilder builder) { |
| super(builder); |
| incrementEntryList = new LinkedList<>(); |
| } |
| |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| monitor.beginTask(MarkerMessages.MarkerView_processUpdates, IProgressMonitor.UNKNOWN); |
| boolean clean= isClean(); |
| if (clean) { |
| clearEntries(); |
| } |
| Collection<MarkerEntry> markerEntries = incrementalEntries(); |
| if (clean) { |
| /* |
| * Unfortunately we cannot lock marker operations between gathering |
| * and updation. We have this code in place only for further |
| * investigation |
| */ |
| clean = !clean(markerEntries, monitor); |
| LinkedList<MarkerUpdate> queue = getUpdatesQueue(); |
| synchronized (queue) { |
| queue.clear(); |
| } |
| } |
| if (monitor.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| if (!clean) { |
| builder.registerTypesToListener(); |
| if (!processUpdates(monitor)) { |
| return Status.CANCEL_STATUS; |
| } |
| } |
| if (!processMarkerEntries(markerEntries, monitor)) { |
| return Status.CANCEL_STATUS; |
| } |
| if (monitor.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| // update with sorted entries |
| updateIncrementalList(markerEntries); |
| |
| if (monitor.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| builder.getUpdateScheduler().scheduleUIUpdate(MarkerUpdateScheduler.SHORT_DELAY); |
| if (monitor.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| builder.setBuilding(false); |
| updateDone(); |
| return Status.OK_STATUS; |
| } |
| |
| /** |
| * Update the list |
| * |
| * @param markerEntries |
| */ |
| private boolean updateIncrementalList(Collection<MarkerEntry> markerEntries) { |
| markerEntries.clear(); |
| Markers clone = builder.getMarkers(); |
| synchronized (clone) { |
| clone = clone.getClone(); |
| } |
| MarkerEntry[] entries = clone.getMarkerEntryArray(); |
| for (int i = 0; i < entries.length; i++) { |
| markerEntries.add(entries[i]); |
| } |
| return true; |
| } |
| |
| /** |
| * Process the incremental updates |
| * |
| * @param monitor |
| */ |
| private boolean processUpdates(IProgressMonitor monitor) { |
| Collection<MarkerEntry> markerEntries = incrementalEntries(); |
| int addCount = 0, removedCount = 0, changedCount = 0, size = 0, newSize = 0; |
| LinkedList<MarkerUpdate> queue = getUpdatesQueue(); |
| MarkerUpdate next = null; |
| do { |
| synchronized (queue) { |
| if (!queue.isEmpty()) { |
| next = queue.removeFirst(); |
| } else { |
| next = null; |
| } |
| } |
| if (monitor.isCanceled() || next == null) { |
| break; |
| } |
| /** |
| * The following performs incremental updation of the markers that |
| * were gathered intially, and keeps them synched at any point with |
| * the markers of interest in Workspace |
| */ |
| // unfortunately marker operations cannot be locked |
| // so locking between gathering of markers and |
| // marker deltas is not possible |
| size = markerEntries.size(); |
| handleRemovedEntries(markerEntries, next.removed, monitor); |
| newSize = markerEntries.size(); |
| removedCount += size - newSize; |
| |
| handleChangedEntries(markerEntries, next.changed, monitor); |
| changedCount += next.changed.size(); |
| |
| size = newSize; |
| handleAddedEntries(markerEntries, next.added, monitor); |
| newSize = markerEntries.size(); |
| removedCount += newSize - size; |
| } while (next != null); |
| boolean[] changeFlags = new boolean[] { addCount > 0, removedCount > 0, |
| changedCount > 0 }; |
| for (int i = 0; i < changeFlags.length; i++) { |
| if (changeFlags[i]) { |
| builder.updateChangeFlags(changeFlags); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @param added |
| * @param monitor |
| */ |
| private void handleAddedEntries(Collection<MarkerEntry> markerEntries, Collection<MarkerEntry> added, |
| IProgressMonitor monitor) { |
| MarkerContentGenerator generator = builder.getGenerator(); |
| Iterator<MarkerEntry> iterator = added.iterator(); |
| while (iterator.hasNext()) { |
| MarkerEntry entry = iterator.next(); |
| if (generator.select(entry)) { |
| markerEntries.add(entry); |
| } |
| } |
| } |
| |
| /** |
| * @param changed |
| * @param monitor |
| */ |
| private void handleChangedEntries(Collection<MarkerEntry> markerEntries, |
| Collection<MarkerEntry> changed, IProgressMonitor monitor) { |
| MarkerContentGenerator generator = builder.getGenerator(); |
| Iterator<MarkerEntry> iterator = changed.iterator(); |
| while (iterator.hasNext()) { |
| MarkerEntry entry = iterator.next(); |
| Iterator<MarkerEntry> iterator2 = markerEntries.iterator(); |
| while (iterator2.hasNext()) { |
| MarkerEntry oldEntry = iterator2.next(); |
| if (oldEntry.getMarker().equals(entry.getMarker())) { |
| iterator2.remove(); |
| } |
| } |
| if (!generator.select(entry)) { |
| iterator.remove(); |
| } |
| } |
| markerEntries.addAll(changed); |
| } |
| |
| /** |
| * @param removed |
| * @param monitor |
| */ |
| private void handleRemovedEntries(Collection<MarkerEntry> markerEntries, |
| Collection<MarkerEntry> removed, IProgressMonitor monitor) { |
| boolean found = false; |
| Iterator<MarkerEntry> iterator = markerEntries.iterator(); |
| while (iterator.hasNext()) { |
| MarkerEntry entry = iterator.next(); |
| found = entry.getStaleState(); |
| if (found) { |
| iterator.remove(); |
| } |
| if (removed.isEmpty()) { |
| continue; |
| } |
| Iterator<MarkerEntry> iterator2 = removed.iterator(); |
| while (iterator2.hasNext()) { |
| MarkerEntry stale = iterator2.next(); |
| if (stale.getMarker().equals(entry.getMarker())) { |
| iterator2.remove(); |
| if (!found) { |
| iterator.remove(); |
| } |
| break; |
| } |
| } |
| } |
| if (removed.isEmpty()) { |
| // TODO: do we check for residuals? |
| return; |
| } |
| // TODO: do we check for residuals? |
| iterator = markerEntries.iterator(); |
| while (iterator.hasNext()) { |
| MarkerEntry entry = iterator.next(); |
| if (entry.getMarker() != null && !entry.getMarker().exists()) { |
| iterator.remove(); |
| } |
| } |
| } |
| |
| /** |
| * Clean |
| */ |
| void clearEntries() { |
| incrementEntryList = new LinkedList<>(); |
| } |
| |
| /** |
| * @return Returns the incrementEntryies. |
| */ |
| Collection<MarkerEntry> incrementalEntries() { |
| return incrementEntryList; |
| } |
| |
| /** |
| * @return the updateQueue that holds the updates and maintains ordering |
| */ |
| LinkedList<MarkerUpdate> getUpdatesQueue() { |
| synchronized (builder.MARKER_INCREMENTAL_UPDATE_FAMILY) { |
| if (updateQueue == null) { |
| updateQueue = new LinkedList<>(); |
| } |
| return updateQueue; |
| } |
| } |
| |
| /** |
| * Add update to the list |
| * |
| * @param update |
| */ |
| void addUpdate(MarkerUpdate update) { |
| LinkedList<MarkerUpdate> updateList = getUpdatesQueue(); |
| synchronized (updateList) { |
| updateList.addLast(update); |
| } |
| } |
| |
| @Override |
| public boolean belongsTo(Object family) { |
| if (family.equals(builder.MARKER_INCREMENTAL_UPDATE_FAMILY)) { |
| return true; |
| } |
| return super.belongsTo(family); |
| } |
| } |