/******************************************************************************* * Copyright (c) 2000, 2016 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 * Mickael Istria (Red Hat Inc.) - Bug 486901 *******************************************************************************/ package org.eclipse.ui.views.tasklist; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IMarkerDelta; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.internal.views.tasklist.TaskListMessages; /** * Task list content provider returns elements that should be * in the task list depending on the selection mode. * It goes directly to the marker manager and retreives * tasks and problems. */ class TaskListContentProvider implements IStructuredContentProvider, IResourceChangeListener { private static final int TASKS = 0; private static final int ERRORS = 1; private static final int WARNINGS = 2; private static final int INFOS = 3; private TaskList taskList; private TableViewer viewer; private IResource input; /* cached counts of tasks, errors, warnings and infos for the visible * markers, maintained incrementally */ private int[] visibleMarkerCounts = null; /* cached count of all markers in workspace matching supported root types * (tasks & problems), maintained incrementally */ private int totalMarkerCount = -1; /** * The constructor. */ public TaskListContentProvider(TaskList taskList) { this.taskList = taskList; this.viewer = taskList.getTableViewer(); } private boolean getFilterOnMarkerLimit() { return taskList.getFilter().getFilterOnMarkerLimit(); } private int getMarkerLimit() { return taskList.getFilter().getMarkerLimit(); } private boolean isMarkerLimitExceeded() { return taskList.isMarkerLimitExceeded(); } private void setMarkerLimitExceeded(boolean markerLimitExceeded) { taskList.setMarkerLimitExceeded(markerLimitExceeded); } /** * Returns a one-line string containing a summary of the number * of visible tasks and problems. */ public String getStatusSummaryVisible() { if (visibleMarkerCounts == null) { return ""; //$NON-NLS-1$ } return NLS.bind(TaskListMessages.TaskList_statusSummaryVisible, sum(visibleMarkerCounts), getStatusSummaryBreakdown(visibleMarkerCounts)); } /** * Returns a one-line string containing a summary of the number * of selected tasks and problems. * * @param selection the current selection */ public String getStatusSummarySelected(IStructuredSelection selection) { int[] selectedMarkerCounts = getMarkerCounts(selection.toList()); return NLS.bind(TaskListMessages.TaskList_statusSummarySelected, sum(selectedMarkerCounts), getStatusSummaryBreakdown(selectedMarkerCounts) ); } /** * Returns a one-line string containing a summary of the number of * given tasks, errors, warnings, and infos. */ private String getStatusSummaryBreakdown(int[] counts) { return NLS.bind( TaskListMessages.TaskList_statusSummaryBreakdown, new Object[] { counts[TASKS], counts[ERRORS], counts[WARNINGS], counts[INFOS] }); } /** * Returns a one-line string containing a summary of the number items * being shown by the filter, for display in the title bar. */ public String getTitleSummary() { if (visibleMarkerCounts == null) { return ""; //$NON-NLS-1$ } int visibleMarkerCount = sum(visibleMarkerCounts); TasksFilter filter = taskList.getFilter(); if (filter.isShowingAll()) { return NLS.bind(TaskListMessages.TaskList_titleSummaryUnfiltered, visibleMarkerCount); } return NLS.bind(TaskListMessages.TaskList_titleSummaryFiltered, visibleMarkerCount, getTotalMarkerCount()); } /** * Returns the sum of the given counts. */ private int sum(int[] counts) { int sum = 0; for (int count : counts) { sum += count; } return sum; } /** * Returns the count of all markers in the workspace which can be shown in * the task list. This is computed once, then maintained incrementally by * the delta processing. */ private int getTotalMarkerCount() { if (totalMarkerCount == -1) { totalMarkerCount = 0; try { IResource root = taskList.getWorkspace().getRoot(); IMarker[] markers = root.findMarkers(null, true, IResource.DEPTH_INFINITE); for (IMarker marker : markers) { if (isRootType(marker)) { ++totalMarkerCount; } } } catch (CoreException e) { // shouldn't happen } } return totalMarkerCount; } /** * Returns whether the given marker is a subtype of one of the root types. */ private boolean isRootType(IMarker marker) { String[] rootTypes = TasksFilter.ROOT_TYPES; for (String rootType : rootTypes) { if (MarkerUtil.isMarkerType(marker, rootType)) { return true; } } return false; } /** * Returns the markers to show in the task list. */ private IMarker[] getMarkers() throws CoreException { IResource[] resources = taskList.getResources(); int l = resources.length; IResource resource; boolean bExists = false; for (int i = 0; i < l; i++) { resource = resources[i]; if (resource != null && resource.exists()) { bExists = true; break; } } if (!bExists) { return new IMarker[0]; } if (taskList.showOwnerProject()) { IResource[] projectResources = new IResource[l]; IResource project; for (int i = 0; i < l; i++) { resource = resources[i]; if (resource != null) { project = resource.getProject(); if (project != null) { projectResources[i] = project; } else { projectResources[i] = resource; } } } resources = projectResources; } int depth = taskList.getResourceDepth(); TasksFilter filter = taskList.getFilter(); Set set = new HashSet(); for (int i = 0; i < l; i++) { resource = resources[i]; if (resource != null) { IMarker[] markers = resource.findMarkers(null, true, depth); for (IMarker marker : markers) { if (filter.select(marker)) { set.add(marker); } } } } IMarker[] result = new IMarker[set.size()]; set.toArray(result); return result; } /** * Returns the number of tasks, errors, warnings, infos * in the given markers. */ private int[] getMarkerCounts(List markers) { int[] markerCounts = new int[4]; Iterator iterator = markers.iterator(); while (iterator.hasNext()) { IMarker marker = (IMarker) iterator.next(); if (MarkerUtil.isMarkerType(marker, IMarker.PROBLEM)) { switch (MarkerUtil.getSeverity(marker)) { case IMarker.SEVERITY_ERROR: ++markerCounts[ERRORS]; break; case IMarker.SEVERITY_WARNING: ++markerCounts[WARNINGS]; break; case IMarker.SEVERITY_INFO: ++markerCounts[INFOS]; break; } } else if (MarkerUtil.isMarkerType(marker, IMarker.TASK)) { ++markerCounts[TASKS]; } } return markerCounts; } /** * Updates the marker counts for the given delta. * Assumptions: * - the delta is either an addition or a removal * - problem severities don't change */ private void updateMarkerCounts(IMarkerDelta markerDelta, int difference) { if (visibleMarkerCounts == null) { return; } if (markerDelta.isSubtypeOf(IMarker.PROBLEM)) { int severity = markerDelta.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING); switch (severity) { case IMarker.SEVERITY_ERROR: visibleMarkerCounts[ERRORS] += difference; break; case IMarker.SEVERITY_WARNING: visibleMarkerCounts[WARNINGS] += difference; break; case IMarker.SEVERITY_INFO: visibleMarkerCounts[INFOS] += difference; break; } } else if (markerDelta.isSubtypeOf(IMarker.TASK)) { visibleMarkerCounts[TASKS] += difference; } } /** * Updates the viewer given the lists of added, removed, and changes * markers. This is called inside an syncExec. */ private void updateViewer(List additions, List removals, List changes) { // The widget may have been destroyed by the time this is run. // Check for this and do nothing if so. Control ctrl = viewer.getControl(); if (ctrl == null || ctrl.isDisposed()) { return; } //update the viewer based on the marker changes. //process removals before additions, to avoid multiple equal elements in //the viewer if (removals.size() > 0) { // Cancel any open cell editor. We assume that the one being edited // is the one being removed. viewer.cancelEditing(); viewer.remove(removals.toArray()); } if (additions.size() > 0) { viewer.add(additions.toArray()); } if (changes.size() > 0) { viewer.update(changes.toArray(), null); } } /** * The visual part that is using this content provider is about * to be disposed. Deallocate all allocated SWT resources. */ @Override public void dispose() { if (input != null) { input.getWorkspace().removeResourceChangeListener(this); input = null; } } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { if (this.input != null) { this.input.getWorkspace().removeResourceChangeListener(this); } this.input = (IResource) newInput; if (this.input != null) { this.input.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); } this.viewer = (TableViewer) viewer; } /** * Returns all the markers that should be shown for * the current settings. */ @Override public Object[] getElements(Object parent) { try { IMarker[] markers = getMarkers(); this.visibleMarkerCounts = getMarkerCounts(Arrays.asList(markers)); if (getFilterOnMarkerLimit() && markers.length > getMarkerLimit()) { if (!isMarkerLimitExceeded()) { setMarkerLimitExceeded(true); viewer.getControl().getDisplay().syncExec(() -> viewer.refresh()); } return new IMarker[0]; } if (isMarkerLimitExceeded()) { setMarkerLimitExceeded(false); viewer.getControl().getDisplay().syncExec(() -> viewer.refresh()); } return markers; } catch (CoreException e) { return new IMarker[0]; } } /** * The workbench has changed. Process the delta and issue updates to the * viewer, inside the UI thread. * * @see IResourceChangeListener#resourceChanged */ @Override public void resourceChanged(final IResourceChangeEvent event) { /* * gather all marker changes from the delta. be sure to do this in the * calling thread, as the delta is destroyed when this method returns */ IMarkerDelta[] markerDeltas = event.findMarkerDeltas(null, true); if (markerDeltas == null) { return; } int oldTotal = totalMarkerCount; final List additions = new ArrayList(); final List removals = new ArrayList(); final List changes = new ArrayList(); for (IMarkerDelta markerDelta : markerDeltas) { if (markerDelta == null) { continue; } int iKind = markerDelta.getKind(); for (String rootType : TasksFilter.ROOT_TYPES) { if (markerDelta.isSubtypeOf(rootType)) { /* * Updates the total count of markers given the applicable * marker deltas. */ if (totalMarkerCount != -1) { switch (iKind) { case IResourceDelta.ADDED: totalMarkerCount++; break; case IResourceDelta.REMOVED: totalMarkerCount--; break; } } /* * Partition the marker deltas into one of the three given * lists depending on * the type of delta (add, remove, or change). * The resulting lists contain the corresponding markers, * not the deltas. * Deltas which are not under the current focus resource are * discarded. * This also updates the marker counts. */ IResource resource = markerDelta.getResource(); if (resource == null) { continue; } boolean affectedBy = taskList.checkResource(resource) && taskList.getFilter().select(markerDelta); if (affectedBy) { IMarker marker = markerDelta.getMarker(); switch (iKind) { case IResourceDelta.ADDED: additions.add(marker); updateMarkerCounts(markerDelta, +1); break; case IResourceDelta.REMOVED: removals.add(marker); updateMarkerCounts(markerDelta, -1); break; case IResourceDelta.CHANGED: changes.add(marker); /* * Assume attribute changes don't affect marker * counts. This is only true if problem severities * can't change. */ break; } } break; } } } if (oldTotal == totalMarkerCount && additions.size() + removals.size() + changes.size() == 0) { // no changes to markers that we care about return; } /* * do the required viewer updates in the UI thread need to use syncExec; * see 1G95PU8: ITPUI:WIN2000 - Changing task description flashes old * description */ viewer.getControl().getDisplay().syncExec(() -> { if (getFilterOnMarkerLimit() && sum(visibleMarkerCounts) > getMarkerLimit()) { if (!isMarkerLimitExceeded()) { setMarkerLimitExceeded(true); viewer.refresh(); } } else if (taskList.isMarkerLimitExceeded()) { setMarkerLimitExceeded(false); viewer.refresh(); } else { updateViewer(additions, removals, changes); } /* Update the task list's status message. * XXX: Quick and dirty solution here. * Would be better to have a separate model for the tasks and * have both the content provider and the task list register for * updates. XXX: Do this inside the syncExec, since we're * talking to status line widget. */ taskList.markersChanged(); }); } }