platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerManager.java
changeset 40 eb3c938c7fef
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/platform35/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/MarkerManager.java	Thu Jul 30 11:56:23 2009 -0500
@@ -0,0 +1,653 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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.core.internal.resources;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.internal.localstore.SafeChunkyInputStream;
+import org.eclipse.core.internal.localstore.SafeFileInputStream;
+import org.eclipse.core.internal.utils.Messages;
+import org.eclipse.core.internal.utils.Policy;
+import org.eclipse.core.internal.watson.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * A marker manager stores and retrieves markers on resources in the workspace.
+ */
+public class MarkerManager implements IManager {
+
+	//singletons
+	private static final MarkerInfo[] NO_MARKER_INFO = new MarkerInfo[0];
+	private static final IMarker[] NO_MARKERS = new IMarker[0];
+	protected MarkerTypeDefinitionCache cache = new MarkerTypeDefinitionCache();
+	private long changeId = 0;
+	protected Map currentDeltas = null;
+	protected final MarkerDeltaManager deltaManager = new MarkerDeltaManager();
+
+	protected Workspace workspace;
+	protected MarkerWriter writer = new MarkerWriter(this);
+
+	/**
+	 * Creates a new marker manager
+	 */
+	public MarkerManager(Workspace workspace) {
+		this.workspace = workspace;
+	}
+
+	/* (non-Javadoc)
+	 * Adds the given markers to the given resource.
+	 * 
+	 * @see IResource#createMarker(String) 
+	 */
+	public void add(IResource resource, MarkerInfo newMarker) throws CoreException {
+		Resource target = (Resource) resource;
+		ResourceInfo info = workspace.getResourceInfo(target.getFullPath(), false, false);
+		target.checkExists(target.getFlags(info), false);
+		info = workspace.getResourceInfo(resource.getFullPath(), false, true);
+		//resource may have been deleted concurrently -- just bail out if this happens
+		if (info == null)
+			return;
+		// set the M_MARKERS_SNAP_DIRTY flag to indicate that this
+		// resource's markers have changed since the last snapshot
+		if (isPersistent(newMarker))
+			info.set(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+		//Concurrency: copy the marker set on modify
+		MarkerSet markers = info.getMarkers(true);
+		if (markers == null)
+			markers = new MarkerSet(1);
+		basicAdd(resource, markers, newMarker);
+		if (!markers.isEmpty())
+			info.setMarkers(markers);
+	}
+
+	/**
+	 * Adds the new markers to the given set of markers.  If added, the markers
+	 * are associated with the specified resource.IMarkerDeltas for Added markers 
+	 * are generated.
+	 */
+	private void basicAdd(IResource resource, MarkerSet markers, MarkerInfo newMarker) throws CoreException {
+		// should always be a new marker.
+		if (newMarker.getId() != MarkerInfo.UNDEFINED_ID) {
+			String message = Messages.resources_changeInAdd;
+			throw new ResourceException(new ResourceStatus(IResourceStatus.INTERNAL_ERROR, resource.getFullPath(), message));
+		}
+		newMarker.setId(workspace.nextMarkerId());
+		markers.add(newMarker);
+		IMarkerSetElement[] changes = new IMarkerSetElement[1];
+		changes[0] = new MarkerDelta(IResourceDelta.ADDED, resource, newMarker);
+		changedMarkers(resource, changes);
+	}
+
+	/**
+	 * Returns the markers in the given set of markers which match the given type.
+	 */
+	protected MarkerInfo[] basicFindMatching(MarkerSet markers, String type, boolean includeSubtypes) {
+		int size = markers.size();
+		if (size <= 0)
+			return NO_MARKER_INFO;
+		List result = new ArrayList(size);
+		IMarkerSetElement[] elements = markers.elements();
+		for (int i = 0; i < elements.length; i++) {
+			MarkerInfo marker = (MarkerInfo) elements[i];
+			// if the type is null then we are looking for all types of markers
+			if (type == null)
+				result.add(marker);
+			else {
+				if (includeSubtypes) {
+					if (cache.isSubtype(marker.getType(), type))
+						result.add(marker);
+				} else {
+					if (marker.getType().equals(type))
+						result.add(marker);
+				}
+			}
+		}
+		size = result.size();
+		if (size <= 0)
+			return NO_MARKER_INFO;
+		return (MarkerInfo[]) result.toArray(new MarkerInfo[size]);
+	}
+
+	protected int basicFindMaxSeverity(MarkerSet markers, String type, boolean includeSubtypes) {
+		int max = -1;
+		int size = markers.size();
+		if (size <= 0)
+			return max;
+		IMarkerSetElement[] elements = markers.elements();
+		for (int i = 0; i < elements.length; i++) {
+			MarkerInfo marker = (MarkerInfo) elements[i];
+			// if the type is null then we are looking for all types of markers
+			if (type == null)
+				max = Math.max(max, getSeverity(marker));
+			else {
+				if (includeSubtypes) {
+					if (cache.isSubtype(marker.getType(), type))
+						max = Math.max(max, getSeverity(marker));
+				} else {
+					if (marker.getType().equals(type))
+						max = Math.max(max, getSeverity(marker));
+				}
+			}
+			if (max >= IMarker.SEVERITY_ERROR) {
+				break;
+			}
+		}
+		return max;
+	}
+
+	private int getSeverity(MarkerInfo marker) {
+		Object o = marker.getAttribute(IMarker.SEVERITY);
+		if (o instanceof Integer) {
+			Integer i = (Integer) o;
+			return i.intValue();
+		}
+		return -1;
+	}
+
+	/**
+	 * Removes markers of the specified type from the given resource.
+	 * Note: this method is protected to avoid creation of a synthetic accessor (it
+	 * is called from an anonymous inner class).
+	 */
+	protected void basicRemoveMarkers(ResourceInfo info, IPathRequestor requestor, String type, boolean includeSubtypes) {
+		MarkerSet markers = info.getMarkers(false);
+		if (markers == null)
+			return;
+		IMarkerSetElement[] matching;
+		IPath path;
+		if (type == null) {
+			// if the type is null, all markers are to be removed.
+			//now we need to crack open the tree
+			path = requestor.requestPath();
+			info = workspace.getResourceInfo(path, false, true);
+			info.setMarkers(null);
+			matching = markers.elements();
+		} else {
+			matching = basicFindMatching(markers, type, includeSubtypes);
+			// if none match, there is nothing to remove
+			if (matching.length == 0)
+				return;
+			//now we need to crack open the tree
+			path = requestor.requestPath();
+			info = workspace.getResourceInfo(path, false, true);
+			//Concurrency: copy the marker set on modify
+			markers = info.getMarkers(true);
+			// remove all the matching markers and also the whole 
+			// set if there are no remaining markers
+			markers.removeAll(matching);
+			info.setMarkers(markers.size() == 0 ? null : markers);
+		}
+		info.set(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+		IMarkerSetElement[] changes = new IMarkerSetElement[matching.length];
+		IResource resource = workspace.getRoot().findMember(path);
+		for (int i = 0; i < matching.length; i++)
+			changes[i] = new MarkerDelta(IResourceDelta.REMOVED, resource, (MarkerInfo) matching[i]);
+		changedMarkers(resource, changes);
+		return;
+	}
+
+	/**
+	 * Adds the markers on the given target which match the specified type to the list.
+	 */
+	protected void buildMarkers(IMarkerSetElement[] markers, IPath path, int type, ArrayList list) {
+		if (markers.length == 0)
+			return;
+		IResource resource = workspace.newResource(path, type);
+		list.ensureCapacity(list.size() + markers.length);
+		for (int i = 0; i < markers.length; i++) {
+			list.add(new Marker(resource, ((MarkerInfo) markers[i]).getId()));
+		}
+	}
+
+	/**
+	 * Markers have changed on the given resource.  Remember the changes for subsequent notification.
+	 */
+	protected void changedMarkers(IResource resource, IMarkerSetElement[] changes) {
+		if (changes == null || changes.length == 0)
+			return;
+		changeId++;
+		if (currentDeltas == null)
+			currentDeltas = deltaManager.newGeneration(changeId);
+		IPath path = resource.getFullPath();
+		MarkerSet previousChanges = (MarkerSet) currentDeltas.get(path);
+		MarkerSet result = MarkerDelta.merge(previousChanges, changes);
+		if (result.size() == 0)
+			currentDeltas.remove(path);
+		else
+			currentDeltas.put(path, result);
+		ResourceInfo info = workspace.getResourceInfo(path, false, true);
+		if (info != null)
+			info.incrementMarkerGenerationCount();
+	}
+
+	/**
+	 * Returns the marker with the given id or <code>null</code> if none is found.
+	 */
+	public IMarker findMarker(IResource resource, long id) {
+		MarkerInfo info = findMarkerInfo(resource, id);
+		return info == null ? null : new Marker(resource, info.getId());
+	}
+
+	/**
+	 * Returns the marker with the given id or <code>null</code> if none is found.
+	 */
+	public MarkerInfo findMarkerInfo(IResource resource, long id) {
+		ResourceInfo info = workspace.getResourceInfo(resource.getFullPath(), false, false);
+		if (info == null)
+			return null;
+		MarkerSet markers = info.getMarkers(false);
+		if (markers == null)
+			return null;
+		return (MarkerInfo) markers.get(id);
+	}
+
+	/**
+	 * Returns all markers of the specified type on the given target, with option
+	 * to search the target's children.
+	 * Passing <code>null</code> for the type specifies a match
+	 * for all types (i.e., <code>null</code> is a wildcard.
+	 */
+	public IMarker[] findMarkers(IResource target, final String type, final boolean includeSubtypes, int depth) {
+		ArrayList result = new ArrayList();
+		doFindMarkers(target, result, type, includeSubtypes, depth);
+		if (result.size() == 0)
+			return NO_MARKERS;
+		return (IMarker[]) result.toArray(new IMarker[result.size()]);
+	}
+
+	/**
+	 * Fills the provided list with all markers of the specified type on the given target, 
+	 * with option to search the target's children.
+	 * Passing <code>null</code> for the type specifies a match
+	 * for all types (i.e., <code>null</code> is a wildcard.
+	 */
+	public void doFindMarkers(IResource target, ArrayList result, final String type, final boolean includeSubtypes, int depth) {
+		//optimize the deep searches with an element tree visitor
+		if (depth == IResource.DEPTH_INFINITE && target.getType() != IResource.FILE)
+			visitorFindMarkers(target.getFullPath(), result, type, includeSubtypes);
+		else
+			recursiveFindMarkers(target.getFullPath(), result, type, includeSubtypes, depth);
+	}
+
+	/**
+	 * Finds the max severity across all problem markers on the given target, 
+	 * with option to search the target's children.
+	 */
+	public int findMaxProblemSeverity(IResource target, String type, boolean includeSubtypes, int depth) {
+		//optimize the deep searches with an element tree visitor
+		if (depth == IResource.DEPTH_INFINITE && target.getType() != IResource.FILE)
+			return visitorFindMaxSeverity(target.getFullPath(), type, includeSubtypes);
+		return recursiveFindMaxSeverity(target.getFullPath(), type, includeSubtypes, depth);
+	}
+
+	public long getChangeId() {
+		return changeId;
+	}
+
+	/**
+	 * Returns the map of all marker deltas since the given change Id.
+	 */
+	public Map getMarkerDeltas(long startChangeId) {
+		return deltaManager.assembleDeltas(startChangeId);
+	}
+
+	/**
+	 * Returns true if this manager has a marker delta record
+	 * for the given marker id, and false otherwise.
+	 */
+	boolean hasDelta(IPath path, long id) {
+		if (currentDeltas == null)
+			return false;
+		MarkerSet set = (MarkerSet) currentDeltas.get(path);
+		if (set == null)
+			return false;
+		return set.get(id) != null;
+	}
+
+	/**
+	 * Returns true if the given marker is persistent, and false
+	 * otherwise.
+	 */
+	public boolean isPersistent(MarkerInfo info) {
+		if (!cache.isPersistent(info.getType()))
+			return false;
+		Object isTransient = info.getAttribute(IMarker.TRANSIENT);
+		return isTransient == null || !(isTransient instanceof Boolean) || !((Boolean) isTransient).booleanValue();
+	}
+
+	/**
+	 * Returns true if <code>type</code> is a sub type of <code>superType</code>.
+	 */
+	public boolean isSubtype(String type, String superType) {
+		return cache.isSubtype(type, superType);
+	}
+
+	public void moved(final IResource source, final IResource destination, int depth) throws CoreException {
+		final int count = destination.getFullPath().segmentCount();
+
+		// we removed from the source and added to the destination
+		IResourceVisitor visitor = new IResourceVisitor() {
+			public boolean visit(IResource resource) {
+				Resource r = (Resource) resource;
+				ResourceInfo info = r.getResourceInfo(false, true);
+				MarkerSet markers = info.getMarkers(false);
+				if (markers == null)
+					return true;
+				info.set(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+				IMarkerSetElement[] removed = new IMarkerSetElement[markers.size()];
+				IMarkerSetElement[] added = new IMarkerSetElement[markers.size()];
+				IPath path = resource.getFullPath().removeFirstSegments(count);
+				path = source.getFullPath().append(path);
+				IResource sourceChild = workspace.newResource(path, resource.getType());
+				IMarkerSetElement[] elements = markers.elements();
+				for (int i = 0; i < elements.length; i++) {
+					// calculate the ADDED delta
+					MarkerInfo markerInfo = (MarkerInfo) elements[i];
+					MarkerDelta delta = new MarkerDelta(IResourceDelta.ADDED, resource, markerInfo);
+					added[i] = delta;
+					// calculate the REMOVED delta
+					delta = new MarkerDelta(IResourceDelta.REMOVED, sourceChild, markerInfo);
+					removed[i] = delta;
+				}
+				changedMarkers(resource, added);
+				changedMarkers(sourceChild, removed);
+				return true;
+			}
+		};
+		destination.accept(visitor, depth, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN);
+	}
+
+	/**
+	 * Adds the markers for a subtree of resources to the list.
+	 */
+	private void recursiveFindMarkers(IPath path, ArrayList list, String type, boolean includeSubtypes, int depth) {
+		ResourceInfo info = workspace.getResourceInfo(path, false, false);
+		if (info == null)
+			return;
+		MarkerSet markers = info.getMarkers(false);
+
+		//add the matching markers for this resource
+		if (markers != null) {
+			IMarkerSetElement[] matching;
+			if (type == null)
+				matching = markers.elements();
+			else
+				matching = basicFindMatching(markers, type, includeSubtypes);
+			buildMarkers(matching, path, info.getType(), list);
+		}
+
+		//recurse
+		if (depth == IResource.DEPTH_ZERO || info.getType() == IResource.FILE)
+			return;
+		if (depth == IResource.DEPTH_ONE)
+			depth = IResource.DEPTH_ZERO;
+		IPath[] children = workspace.getElementTree().getChildren(path);
+		for (int i = 0; i < children.length; i++) {
+			recursiveFindMarkers(children[i], list, type, includeSubtypes, depth);
+		}
+	}
+
+	/**
+	 * Finds the max severity across problem markers for a subtree of resources.
+	 */
+	private int recursiveFindMaxSeverity(IPath path, String type, boolean includeSubtypes, int depth) {
+		ResourceInfo info = workspace.getResourceInfo(path, false, false);
+		if (info == null)
+			return -1;
+		MarkerSet markers = info.getMarkers(false);
+
+		//add the matching markers for this resource
+		int max = -1;
+		if (markers != null) {
+			max = basicFindMaxSeverity(markers, type, includeSubtypes);
+			if (max >= IMarker.SEVERITY_ERROR) {
+				return max;
+			}
+		}
+
+		//recurse
+		if (depth == IResource.DEPTH_ZERO || info.getType() == IResource.FILE)
+			return max;
+		if (depth == IResource.DEPTH_ONE)
+			depth = IResource.DEPTH_ZERO;
+		IPath[] children = workspace.getElementTree().getChildren(path);
+		for (int i = 0; i < children.length; i++) {
+			max = Math.max(max, recursiveFindMaxSeverity(children[i], type, includeSubtypes, depth));
+			if (max >= IMarker.SEVERITY_ERROR) {
+				break;
+			}
+		}
+		return max;
+	}
+
+	/**
+	 * Adds the markers for a subtree of resources to the list.
+	 */
+	private void recursiveRemoveMarkers(final IPath path, String type, boolean includeSubtypes, int depth) {
+		ResourceInfo info = workspace.getResourceInfo(path, false, false);
+		if (info == null)//phantoms don't have markers
+			return;
+		IPathRequestor requestor = new IPathRequestor() {
+			public String requestName() {
+				return path.lastSegment();
+			}
+
+			public IPath requestPath() {
+				return path;
+			}
+		};
+		basicRemoveMarkers(info, requestor, type, includeSubtypes);
+		//recurse
+		if (depth == IResource.DEPTH_ZERO || info.getType() == IResource.FILE)
+			return;
+		if (depth == IResource.DEPTH_ONE)
+			depth = IResource.DEPTH_ZERO;
+		IPath[] children = workspace.getElementTree().getChildren(path);
+		for (int i = 0; i < children.length; i++) {
+			recursiveRemoveMarkers(children[i], type, includeSubtypes, depth);
+		}
+	}
+
+	/**
+	 * Removes the specified marker 
+	 */
+	public void removeMarker(IResource resource, long id) {
+		MarkerInfo markerInfo = findMarkerInfo(resource, id);
+		if (markerInfo == null)
+			return;
+		ResourceInfo info = ((Workspace) resource.getWorkspace()).getResourceInfo(resource.getFullPath(), false, true);
+		//Concurrency: copy the marker set on modify
+		MarkerSet markers = info.getMarkers(true);
+		int size = markers.size();
+		markers.remove(markerInfo);
+		// if that was the last marker remove the set to save space.
+		info.setMarkers(markers.size() == 0 ? null : markers);
+		// if we actually did remove a marker, post a delta for the change.
+		if (markers.size() != size) {
+			if (isPersistent(markerInfo))
+				info.set(ICoreConstants.M_MARKERS_SNAP_DIRTY);
+			IMarkerSetElement[] change = new IMarkerSetElement[] {new MarkerDelta(IResourceDelta.REMOVED, resource, markerInfo)};
+			changedMarkers(resource, change);
+		}
+	}
+
+	/**
+	 * Remove all markers for the given resource to the specified depth.
+	 */
+	public void removeMarkers(IResource resource, int depth) {
+		removeMarkers(resource, null, false, depth);
+	}
+
+	/**
+	 * Remove all markers with the given type from the node at the given path.
+	 * Passing <code>null</code> for the type specifies a match
+	 * for all types (i.e., <code>null</code> is a wildcard.
+	 */
+	public void removeMarkers(IResource target, final String type, final boolean includeSubtypes, int depth) {
+		if (depth == IResource.DEPTH_INFINITE && target.getType() != IResource.FILE)
+			visitorRemoveMarkers(target.getFullPath(), type, includeSubtypes);
+		else
+			recursiveRemoveMarkers(target.getFullPath(), type, includeSubtypes, depth);
+	}
+
+	/**
+	 * Reset the marker deltas up to but not including the given start Id.
+	 */
+	public void resetMarkerDeltas(long startId) {
+		currentDeltas = null;
+		deltaManager.resetDeltas(startId);
+	}
+
+	public void restore(IResource resource, boolean generateDeltas, IProgressMonitor monitor) throws CoreException {
+		// first try and load the last saved file, then apply the snapshots
+		restoreFromSave(resource, generateDeltas);
+		restoreFromSnap(resource);
+	}
+
+	protected void restoreFromSave(IResource resource, boolean generateDeltas) throws CoreException {
+		IPath sourceLocation = workspace.getMetaArea().getMarkersLocationFor(resource);
+		IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(sourceLocation);
+		java.io.File sourceFile = new java.io.File(sourceLocation.toOSString());
+		java.io.File tempFile = new java.io.File(tempLocation.toOSString());
+		if (!sourceFile.exists() && !tempFile.exists())
+			return;
+		try {
+			DataInputStream input = new DataInputStream(new SafeFileInputStream(sourceLocation.toOSString(), tempLocation.toOSString()));
+			try {
+				MarkerReader reader = new MarkerReader(workspace);
+				reader.read(input, generateDeltas);
+			} finally {
+				input.close();
+			}
+		} catch (Exception e) {
+			//don't let runtime exceptions such as ArrayIndexOutOfBounds prevent startup
+			String msg = NLS.bind(Messages.resources_readMeta, sourceLocation);
+			throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, sourceLocation, msg, e);
+		}
+	}
+
+	protected void restoreFromSnap(IResource resource) {
+		IPath sourceLocation = workspace.getMetaArea().getMarkersSnapshotLocationFor(resource);
+		if (!sourceLocation.toFile().exists())
+			return;
+		try {
+			DataInputStream input = new DataInputStream(new SafeChunkyInputStream(sourceLocation.toFile()));
+			try {
+				MarkerSnapshotReader reader = new MarkerSnapshotReader(workspace);
+				while (true)
+					reader.read(input);
+			} catch (EOFException eof) {
+				// ignore end of file
+			} finally {
+				input.close();
+			}
+		} catch (Exception e) {
+			// only log the exception, we should not fail restoring the snapshot
+			String msg = NLS.bind(Messages.resources_readMeta, sourceLocation);
+			Policy.log(new ResourceStatus(IResourceStatus.FAILED_READ_METADATA, sourceLocation, msg, e));
+		}
+	}
+
+	public void save(ResourceInfo info, IPathRequestor requestor, DataOutputStream output, List list) throws IOException {
+		writer.save(info, requestor, output, list);
+	}
+
+	/* (non-Javadoc)
+	 * @see IManager#shutdown(IProgressMonitor)
+	 */
+	public void shutdown(IProgressMonitor monitor) {
+		// do nothing
+	}
+
+	public void snap(ResourceInfo info, IPathRequestor requestor, DataOutputStream output) throws IOException {
+		writer.snap(info, requestor, output);
+	}
+
+	/* (non-Javadoc)
+	 * @see IManager#startup(IProgressMonitor)
+	 */
+	public void startup(IProgressMonitor monitor) {
+		// do nothing
+	}
+
+	/**
+	 * Adds the markers for a subtree of resources to the list.
+	 */
+	private void visitorFindMarkers(IPath path, final ArrayList list, final String type, final boolean includeSubtypes) {
+		IElementContentVisitor visitor = new IElementContentVisitor() {
+			public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
+				ResourceInfo info = (ResourceInfo) elementContents;
+				if (info == null)
+					return false;
+				MarkerSet markers = info.getMarkers(false);
+
+				//add the matching markers for this resource
+				if (markers != null) {
+					IMarkerSetElement[] matching;
+					if (type == null)
+						matching = markers.elements();
+					else
+						matching = basicFindMatching(markers, type, includeSubtypes);
+					buildMarkers(matching, requestor.requestPath(), info.getType(), list);
+				}
+				return true;
+			}
+		};
+		new ElementTreeIterator(workspace.getElementTree(), path).iterate(visitor);
+	}
+
+	/**
+	 * Finds the max severity across problem markers for a subtree of resources.
+	 */
+	private int visitorFindMaxSeverity(IPath path, final String type, final boolean includeSubtypes) {
+		class MaxSeverityVisitor implements IElementContentVisitor {
+			int max = -1;
+
+			public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
+				// bail if an earlier sibling already hit the max
+				if (max >= IMarker.SEVERITY_ERROR) {
+					return false;
+				}
+				ResourceInfo info = (ResourceInfo) elementContents;
+				if (info == null)
+					return false;
+				MarkerSet markers = info.getMarkers(false);
+
+				//add the matching markers for this resource
+				if (markers != null) {
+					max = Math.max(max, basicFindMaxSeverity(markers, type, includeSubtypes));
+				}
+				return max < IMarker.SEVERITY_ERROR;
+			}
+		}
+		MaxSeverityVisitor visitor = new MaxSeverityVisitor();
+		new ElementTreeIterator(workspace.getElementTree(), path).iterate(visitor);
+		return visitor.max;
+	}
+
+	/**
+	 * Adds the markers for a subtree of resources to the list.
+	 */
+	private void visitorRemoveMarkers(IPath path, final String type, final boolean includeSubtypes) {
+		IElementContentVisitor visitor = new IElementContentVisitor() {
+			public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) {
+				ResourceInfo info = (ResourceInfo) elementContents;
+				if (info == null)
+					return false;
+				basicRemoveMarkers(info, requestor, type, includeSubtypes);
+				return true;
+			}
+		};
+		new ElementTreeIterator(workspace.getElementTree(), path).iterate(visitor);
+	}
+}