blob: d6149877de7a462cc11cede402223f97581aa54e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 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.views.markers.internal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.mapping.ResourceMapping;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.PlatformUI;
public class MarkerFilter implements Cloneable {
private static final String TAG_ENABLED = "enabled"; //$NON-NLS-1$
private static final String TAG_ON_RESOURCE = "onResource"; //$NON-NLS-1$
private static final String TAG_SELECTED_TYPES = "selectedType"; //$NON-NLS-1$
private static final String TAG_WORKING_SET = "workingSet"; //$NON-NLS-1$
private static final String TAG_TYPES_DELIMITER = ":"; //$NON-NLS-1$
/**
* New attribute to handle the selection status of marker types.
*/
private static final String TAG_SELECTION_STATUS = "selectionStatus"; //$NON-NLS-1$
/**
* Attribute status true.
*/
private static final String SELECTED_FALSE = "false"; //$NON-NLS-1$
/**
* Attribute status false.
*/
private static final String SELECTED_TRUE = "true"; //$NON-NLS-1$
public static final int ON_ANY = 0;
public static final int ON_SELECTED_ONLY = 1;
public static final int ON_SELECTED_AND_CHILDREN = 2;
public static final int ON_ANY_IN_SAME_CONTAINER = 3;
public static final int ON_WORKING_SET = 4;
static final int DEFAULT_ON_RESOURCE = ON_ANY;
static final boolean DEFAULT_ACTIVATION_STATUS = true;
protected List rootTypes = new ArrayList();
protected List selectedTypes = new ArrayList();
protected IWorkingSet workingSet;
protected int onResource;
protected boolean enabled;
private IResource[] focusResource;
private Set cachedWorkingSet;
// The human readable name for the filter
private String name;
/**
* Create a new instance of the receiver.
*
* @param filterName
* The human readable name for the filter
* @param rootTypes
* The types this filter will be applied to
*/
MarkerFilter(String filterName, String[] rootTypes) {
name = filterName;
for (int i = 0; i < rootTypes.length; i++) {
MarkerType type = MarkerTypesModel.getInstance().getType(rootTypes[i]);
if (!this.rootTypes.contains(type))
this.rootTypes.add(type);
}
resetState();
}
/**
* List all types known to this MarkerFilter.
*
* @param types
* list to be filled in with types
*/
public void addAllSubTypes(List types) {
for (int i = 0; i < rootTypes.size(); i++) {
MarkerType rootType = (MarkerType) rootTypes.get(i);
addAllSubTypes(types, rootType);
}
}
private void addAllSubTypes(List types, MarkerType type) {
if (type == null)
return;
if (!types.contains(type))
types.add(type);
MarkerType[] subTypes = type.getSubtypes();
for (int i = 0; i < subTypes.length; i++)
addAllSubTypes(types, subTypes[i]);
}
/**
* Adds all markers in the given set of resources to the given list
*
* @param resultList
* @param resources
* @param markerTypeId
* @param depth
* @throws CoreException
*/
private List findMarkers(IResource[] resources, int depth, int limit,
IProgressMonitor mon, boolean ignoreExceptions)
throws CoreException {
if (resources == null) {
return Collections.EMPTY_LIST;
}
List resultList = new ArrayList(resources.length * 2);
// Optimization: if a type appears in the selectedTypes list along with
// all of its
// subtypes, then combine these in a single search.
// List of types that haven't been replaced by one of their supertypes
HashSet typesToSearch = new HashSet(selectedTypes.size());
// List of types that appeared in selectedTypes along with all of their
// subtypes
HashSet includeAllSubtypes = new HashSet(selectedTypes.size());
typesToSearch.addAll(selectedTypes);
Iterator iter = selectedTypes.iterator();
while (iter.hasNext()) {
MarkerType type = (MarkerType) iter.next();
Collection subtypes = Arrays.asList(type.getAllSubTypes());
if (selectedTypes.containsAll(subtypes)) {
typesToSearch.removeAll(subtypes);
includeAllSubtypes.add(type);
}
}
mon.beginTask(MarkerMessages.MarkerFilter_searching, typesToSearch
.size()
* resources.length);
// Use this hash set to determine if there are any resources in the
// list that appear along with their parent.
HashSet resourcesToSearch = new HashSet();
// Insert all the resources into the hashset
for (int idx = 0; idx < resources.length; idx++) {
IResource next = resources[idx];
if (!next.exists()) {
continue;
}
if (resourcesToSearch.contains(next)) {
mon.worked(typesToSearch.size());
} else {
resourcesToSearch.add(next);
}
}
// Iterate through all the selected resources
for (int resourceIdx = 0; resourceIdx < resources.length; resourceIdx++) {
iter = typesToSearch.iterator();
IResource resource = resources[resourceIdx];
// Skip resources that don't exist
if (!resource.isAccessible()) {
continue;
}
if (depth == IResource.DEPTH_INFINITE) {
// Determine if any parent of this resource is also in our
// filter
IResource parent = resource.getParent();
boolean found = false;
while (parent != null) {
if (resourcesToSearch.contains(parent)) {
found = true;
}
parent = parent.getParent();
}
// If a parent of this resource is also in the filter, we can
// skip it
// because we'll pick up its markers when we search the parent.
if (found) {
continue;
}
}
// Iterate through all the marker types
while (iter.hasNext()) {
MarkerType markerType = (MarkerType) iter.next();
// Only search for subtypes of the marker if we found all of its
// subtypes in the filter criteria.
IMarker[] markers = resource.findMarkers(markerType.getId(),
includeAllSubtypes.contains(markerType), depth);
mon.worked(1);
for (int idx = 0; idx < markers.length; idx++) {
ConcreteMarker marker;
try {
marker = MarkerList.createMarker(markers[idx]);
} catch (CoreException e) {
if (ignoreExceptions) {
continue;
} else {
throw e;
}
}
if (limit != -1 && resultList.size() >= limit) {
return resultList;
}
if (selectMarker(marker)) {
resultList.add(marker);
}
}
}
}
mon.done();
return resultList;
}
/**
* Subclasses should override to determine if the given marker passes the
* filter.
*
* @param marker
* @return <code>true</code> if the marker passes the filter and
* <code>false</code> otherwise
*/
protected boolean selectMarker(ConcreteMarker marker) {
return true;
}
/**
* Searches the workspace for markers that pass this filter.
*
* @return Collection of markers.
*/
Collection findMarkers(IProgressMonitor mon, boolean ignoreExceptions)
throws CoreException {
List unfiltered = Collections.EMPTY_LIST;
if (!isEnabled()) {
unfiltered = findMarkers(new IResource[] { ResourcesPlugin
.getWorkspace().getRoot() }, IResource.DEPTH_INFINITE, -1,
mon, ignoreExceptions);
} else {
// int limit = getFilterOnMarkerLimit() ? getMarkerLimit() + 1 : -1;
int limit = -1;
switch (getOnResource()) {
case ON_ANY: {
unfiltered = findMarkers(new IResource[] { ResourcesPlugin
.getWorkspace().getRoot() }, IResource.DEPTH_INFINITE,
limit, mon, ignoreExceptions);
break;
}
case ON_SELECTED_ONLY: {
unfiltered = findMarkers(focusResource, IResource.DEPTH_ZERO,
limit, mon, ignoreExceptions);
break;
}
case ON_SELECTED_AND_CHILDREN: {
unfiltered = findMarkers(focusResource,
IResource.DEPTH_INFINITE, limit, mon, ignoreExceptions);
break;
}
case ON_ANY_IN_SAME_CONTAINER: {
unfiltered = findMarkers(getProjects(focusResource),
IResource.DEPTH_INFINITE, limit, mon, ignoreExceptions);
break;
}
case ON_WORKING_SET: {
unfiltered = findMarkers(getResourcesInWorkingSet(),
IResource.DEPTH_INFINITE, limit, mon, ignoreExceptions);
}
}
}
if (unfiltered == null) {
unfiltered = Collections.EMPTY_LIST;
}
return unfiltered;
}
/**
* Return the resources in the working set. If it is empty then
* return the workspace root.
* @return IResource[]
*/
IResource[] getResourcesInWorkingSet() {
if (workingSet == null) {
return new IResource[0];
}
if (workingSet.isEmpty()) {
return new IResource[] { ResourcesPlugin.getWorkspace().getRoot() };
}
IAdaptable[] elements = workingSet.getElements();
List result = new ArrayList(elements.length);
for (int idx = 0; idx < elements.length; idx++) {
IResource next = (IResource) elements[idx]
.getAdapter(IResource.class);
if (next != null) {
result.add(next);
}
}
return (IResource[]) result.toArray(new IResource[result.size()]);
}
/**
* Returns a set of strings representing the full pathnames to every
* resource directly or indirectly contained in the working set. A resource
* is in the working set iff its path name can be found in this set.
*
* @return Set
*/
private Set getWorkingSetAsSetOfPaths() {
if (cachedWorkingSet == null) {
HashSet result = new HashSet();
addResourcesAndChildren(result, getResourcesInWorkingSet());
cachedWorkingSet = result;
}
return cachedWorkingSet;
}
/***************************************************************************
* Adds the paths of all resources in the given array to the given set.
*/
private void addResourcesAndChildren(HashSet result, IResource[] resources) {
for (int idx = 0; idx < resources.length; idx++) {
IResource currentResource = resources[idx];
result.add(currentResource.getFullPath().toString());
if (currentResource instanceof IContainer) {
IContainer cont = (IContainer) currentResource;
try {
addResourcesAndChildren(result, cont.members());
} catch (CoreException e) {
// Ignore errors
}
}
}
}
/**
* Returns the set of projects that contain the given set of resources.
*
* @param resources
* @return
*/
static IProject[] getProjects(IResource[] resources) {
if (resources == null) {
return new IProject[0];
}
Collection projects = getProjectsAsCollection(resources);
return (IProject[]) projects.toArray(new IProject[projects.size()]);
}
/**
* Return the projects for the elements.
*
* @param elements
* collection of IResource or IResourceMapping
* @return Collection of IProject
*/
static Collection getProjectsAsCollection(Object[] elements) {
HashSet projects = new HashSet();
for (int idx = 0; idx < elements.length; idx++) {
if (elements[idx] instanceof IResource)
projects.add(((IResource) elements[idx]).getProject());
else {
IProject[] mappingProjects = (((ResourceMapping) elements[idx])
.getProjects());
for (int i = 0; i < mappingProjects.length; i++) {
projects.add(mappingProjects[i]);
}
}
}
return projects;
}
public boolean select(ConcreteMarker marker) {
if (!isEnabled()) {
return true;
}
return selectByType(marker) && selectBySelection(marker)
&& selectMarker(marker);
}
private boolean selectByType(ConcreteMarker marker) {
return selectedTypes.contains(MarkerTypesModel.getInstance().getType(marker.getType()));
}
/**
* Returns whether the specified marker should be filter out or not.
*
* @param marker
* the marker to test
* @return true=the marker should not be filtered out false=the marker
* should be filtered out
*/
private boolean selectBySelection(ConcreteMarker marker) {
if (onResource == ON_ANY || marker == null)
return true;
if (focusResource == null)
return true;
IResource resource = marker.getResource();
if (onResource == ON_WORKING_SET) {
if (resource != null)
return isEnclosed(resource);
} else if (onResource == ON_ANY_IN_SAME_CONTAINER) {
IProject project = resource.getProject();
if (project == null) {
return false;
}
for (int i = 0; i < focusResource.length; i++) {
IProject selectedProject = focusResource[i].getProject();
if (selectedProject == null) {
continue;
}
if (project.equals(selectedProject))
return true;
}
} else if (onResource == ON_SELECTED_ONLY) {
for (int i = 0; i < focusResource.length; i++) {
if (resource.equals(focusResource[i]))
return true;
}
} else if (onResource == ON_SELECTED_AND_CHILDREN) {
for (int i = 0; i < focusResource.length; i++) {
IResource parentResource = resource;
while (parentResource != null) {
if (parentResource.equals(focusResource[i]))
return true;
parentResource = parentResource.getParent();
}
}
}
return false;
}
/**
* Returns if the given resource is enclosed by a working set element.
* Previous versions of this method used IContainmentAdapter for containment
* tests. For performance reasons, this is no longer possible. Code that
* relies on this behavior should be updated appropriately.
*
* @param element
* resource to test for enclosure by a working set element
* @return true if element is enclosed by a working set element and false
* otherwise.
*/
private boolean isEnclosed(IResource element) {
if (workingSet == null)
return false;
if (workingSet.isEmpty())
return true; // Everything is in an empty working set
Set workingSetPaths = getWorkingSetAsSetOfPaths();
return workingSetPaths.contains(element.getFullPath().toString());
}
/**
* <ul>
* <li><code>MarkerFilter.ON_ANY</code> if showing items associated with
* any resource.</li>
* <li><code>MarkerFilter.ON_SELECTED_ONLY</code> if showing items
* associated with the selected resource within the workbench.</li>
* <li><code>MarkerFilter.ON_SELECTED_AND_CHILDREN</code> if showing
* items associated with the selected resource within the workbench and its
* children.</li>
* <li><code>MarkerFilter.ON_ANY_OF_SAME_PROJECT</code> if showing items
* in the same project as the selected resource within the workbench.</li>
* <li><code>MarkerFilter.ON_WORKING_SET</code> if showing items in some
* working set.</li>
* </ul>
*/
public int getOnResource() {
return onResource;
}
/**
* Sets the type of filtering by selection.
*
* @param onResource
* must be one of:
* <ul>
* <li><code>MarkerFilter.ON_ANY_RESOURCE</code></li>
* <li><code>MarkerFilter.ON_SELECTED_RESOURCE_ONLY</code></li>
* <li><code>MarkerFilter.ON_SELECTED_RESOURCE_AND_CHILDREN</code></li>
* <li><code>MarkerFilter.ON_ANY_RESOURCE_OF_SAME_PROJECT</code></li>
* <li><code>MarkerFilter.ON_WORKING_SET</code></li>
* </ul>
*/
void setOnResource(int onResource) {
if (onResource >= ON_ANY && onResource <= ON_WORKING_SET)
this.onResource = onResource;
}
/**
* @return the selected resource(s) withing the workbench.
*/
IResource[] getFocusResource() {
return focusResource;
}
/**
* Sets the focused resources.
*/
public void setFocusResource(IResource[] resources) {
focusResource = resources;
}
/**
* @return
* <ul>
* <li><code>true</code> if the filter is enabled.</li>
* <li><code>false</code> if the filter is not enabled.</li>
* </ul>
*/
public boolean isEnabled() {
return enabled;
}
/**
* <b>Warning:</b> for internal package use only. Return the root marker
* types.
*
* @return the root marker types.
*/
public List getRootTypes() {
return rootTypes;
}
/**
* <b>Warning:</b> for internal package use only. Return the selected
* types.
*
* @return the selected marker types to be displayed.
*/
public List getSelectedTypes() {
return selectedTypes;
}
/**
* Find the typeModel entry that matches id.
*
* @param id
* the ID for a marker type
* @return MarkerType or <code>null</code> if it is not found.
*/
public MarkerType getMarkerType(String id) {
return MarkerTypesModel.getInstance().getType(id);
}
/**
* @return the current working set or <code>null</code> if no working set
* is defined.
*/
IWorkingSet getWorkingSet() {
return workingSet;
}
/**
* Sets the enablement state of the filter.
*/
void setEnabled(boolean enabled) {
this.enabled = enabled;
}
/**
* Sets the current working set.
*/
void setWorkingSet(IWorkingSet workingSet) {
this.workingSet = workingSet;
cachedWorkingSet = null;
}
/**
* Reset to the default state.
*/
void resetState() {
enabled = DEFAULT_ACTIVATION_STATUS;
onResource = DEFAULT_ON_RESOURCE;
selectedTypes.clear();
addAllSubTypes(selectedTypes);
setWorkingSet(null);
}
/**
* Restore the state in the memento.
*
* @param memento
*/
public final void restoreState(IMemento memento) {
resetState();
restoreFilterSettings(memento);
}
/**
* Restore the state of the receiver in the supplied settings. This is kept
* for backwards compatibility with 3.1 dialog settings.
*
* @param settings
*/
public void restoreFilterSettings(IDialogSettings settings) {
resetState();
String setting = settings.get(TAG_ENABLED);
if (setting != null)
enabled = Boolean.valueOf(setting).booleanValue();
setting = settings.get(TAG_ON_RESOURCE);
if (setting != null) {
try {
onResource = Integer.parseInt(setting);
} catch (NumberFormatException eNumberFormat) {
}
}
// new selection list attribute
// format is "id:(true|false):"
setting = settings.get(TAG_SELECTION_STATUS);
if (setting != null) {
selectedTypes.clear();
// get the complete list of types
List newTypes = new ArrayList();
addAllSubTypes(newTypes);
StringTokenizer stringTokenizer = new StringTokenizer(setting);
while (stringTokenizer.hasMoreTokens()) {
String id = stringTokenizer.nextToken(TAG_TYPES_DELIMITER);
String status = null;
if (stringTokenizer.hasMoreTokens()) {
status = stringTokenizer.nextToken(TAG_TYPES_DELIMITER);
}
MarkerType markerType = MarkerTypesModel.getInstance().getType(id);
if (markerType != null) {
newTypes.remove(markerType);
// add the type to the selected list
if (!SELECTED_FALSE.equals(status)
&& !selectedTypes.contains(markerType)) {
selectedTypes.add(markerType);
}
}
}
// any types we know about that weren't either true or
// false in the selection attribute are new. By default,
// new marker types will be selected=true
for (int i = 0; i < newTypes.size(); ++i) {
selectedTypes.add(newTypes.get(i));
}
} else {
// the settings didn't contain the new selection attribute
// so check for the old selection attribute.
// format is just "id:"
setting = settings.get(TAG_SELECTED_TYPES);
if (setting != null) {
generateSelectedTypes(setting);
}
}
setting = settings.get(TAG_WORKING_SET);
if (setting != null)
setWorkingSet(PlatformUI.getWorkbench().getWorkingSetManager()
.getWorkingSet(setting));
}
/**
* Set the selected types based on the value.
*
* @param selectedTypesValue
*/
void generateSelectedTypes(String selectedTypesValue) {
selectedTypes.clear();
StringTokenizer stringTokenizer = new StringTokenizer(
selectedTypesValue);
while (stringTokenizer.hasMoreTokens()) {
MarkerType markerType = getMarkerType(stringTokenizer
.nextToken(TAG_TYPES_DELIMITER));
if (markerType != null && !selectedTypes.contains(markerType))
selectedTypes.add(markerType);
}
}
/**
* Find the markerType matching typeName
*
* @param typeName
* @return MarkerType
*/
MarkerType findMarkerType(String typeName) {
return MarkerTypesModel.getInstance().getType(typeName);
}
/**
* Restore the state of the receiver in the supplied settings.
*
* @param memento
*/
protected void restoreFilterSettings(IMemento memento) {
String setting = memento.getString(TAG_ENABLED);
if (setting != null)
enabled = Boolean.valueOf(setting).booleanValue();
Integer resourceSetting = memento.getInteger(TAG_ON_RESOURCE);
if (resourceSetting != null) {
onResource = resourceSetting.intValue();
}
// new selection list attribute
// format is "id:(true|false):"
setting = memento.getString(TAG_SELECTION_STATUS);
if (setting != null) {
selectedTypes.clear();
// get the complete list of types
List newTypes = new ArrayList();
addAllSubTypes(newTypes);
StringTokenizer stringTokenizer = new StringTokenizer(setting);
while (stringTokenizer.hasMoreTokens()) {
String id = stringTokenizer.nextToken(TAG_TYPES_DELIMITER);
String status = null;
if (stringTokenizer.hasMoreTokens()) {
status = stringTokenizer.nextToken(TAG_TYPES_DELIMITER);
}
MarkerType markerType = MarkerTypesModel.getInstance().getType(id);
if (markerType != null) {
newTypes.remove(markerType);
// add the type to the selected list
if (!SELECTED_FALSE.equals(status)
&& !selectedTypes.contains(markerType)) {
selectedTypes.add(markerType);
}
}
}
// any types we know about that weren't either true or
// false in the selection attribute are new. By default,
// new marker types will be selected=true
for (int i = 0; i < newTypes.size(); ++i) {
selectedTypes.add(newTypes.get(i));
}
} else {
// the settings didn't contain the new selection attribute
// so check for the old selection attribute.
// format is just "id:"
setting = memento.getString(TAG_SELECTED_TYPES);
if (setting != null) {
generateSelectedTypes(setting);
}
}
setting = memento.getString(TAG_WORKING_SET);
if (setting != null)
setWorkingSet(PlatformUI.getWorkbench().getWorkingSetManager()
.getWorkingSet(setting));
}
/**
* Save the filter settings for the receiver.
*
* @param settings
*/
public void saveFilterSettings(IMemento settings) {
settings.putString(TAG_ENABLED, String.valueOf(enabled));
settings.putInteger(TAG_ON_RESOURCE, onResource);
String markerTypeIds = ""; //$NON-NLS-1$
List includedTypes = new ArrayList();
addAllSubTypes(includedTypes);
for (int i = 0; i < includedTypes.size(); i++) {
MarkerType markerType = (MarkerType) includedTypes.get(i);
markerTypeIds += markerType.getId() + TAG_TYPES_DELIMITER;
if (selectedTypes.contains(markerType)) {
markerTypeIds += SELECTED_TRUE + TAG_TYPES_DELIMITER;
} else {
markerTypeIds += SELECTED_FALSE + TAG_TYPES_DELIMITER;
}
}
settings.putString(TAG_SELECTION_STATUS, markerTypeIds);
if (workingSet != null)
settings.putString(TAG_WORKING_SET, workingSet.getName());
}
/**
* Get the name of the receiver
*
* @return String
*/
public String getName() {
return name;
}
/**
* Make a clone of the receiver.
*
* @return MarkerFilter
* @throws CloneNotSupportedException
*/
public MarkerFilter makeClone() throws CloneNotSupportedException {
return (MarkerFilter) this.clone();
}
/**
* Set the selected types.
*
* @param selectedTypes
* List of MarkerType.
*/
public void setSelectedTypes(List selectedTypes) {
this.selectedTypes = selectedTypes;
}
}