package org.robotframework.ide.eclipse.main.plugin.navigator;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
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.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.IDecoration;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ILightweightLabelDecorator;
import org.eclipse.jface.viewers.LabelProviderChangedEvent;
import org.robotframework.ide.eclipse.main.plugin.RedImages;
import org.robotframework.ide.eclipse.main.plugin.RedPlugin;
public class MarkerLabelDecorator implements ILightweightLabelDecorator, IResourceChangeListener {
private final List<ILabelProviderListener> listeners = new ArrayList<ILabelProviderListener>();
private final List<IMarker> markersCache = new ArrayList<IMarker>();
private static final ImageDescriptor ERROR = RedImages.getErrorImage();
private static final ImageDescriptor WARNING = RedImages.getWarningImage();
public MarkerLabelDecorator() {
ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
}
@Override
public void decorate(final Object element, final IDecoration decoration) {
if (element instanceof IResource) {
final IResource resource = (IResource) element;
if (resource.getProject().isOpen()) {
refreshMarkersCache(resource);
if (markersCache.isEmpty()) {
return;
}
final ImageDescriptor overlay = getOverlay(resource);
if (overlay != null) {
decoration.addOverlay(overlay);
}
}
}
}
private ImageDescriptor getOverlay(final IResource resource) {
if (resource instanceof IProject) {
return determineOverlay();
} else if (resource instanceof IFolder) {
return determineOverlay((IFolder) resource);
} else if (resource instanceof IFile) {
return determineOverlay((IFile) resource);
}
return null;
}
private ImageDescriptor determineOverlay() {
for (final IMarker marker : markersCache) {
if (isMarkerOfSeverity(marker, IMarker.SEVERITY_ERROR)) {
return ERROR;
}
}
return WARNING;
}
private ImageDescriptor determineOverlay(final IFolder folder) {
boolean hasWarning = false;
for (final IMarker marker : markersCache) {
if (isDescendentOf(marker, folder)) {
if (isMarkerOfSeverity(marker, IMarker.SEVERITY_ERROR)) {
return ERROR;
}
hasWarning |= isMarkerOfSeverity(marker, IMarker.SEVERITY_WARNING);
}
}
return hasWarning ? WARNING : null;
}
private ImageDescriptor determineOverlay(final IFile file) {
boolean hasWarning = false;
for (final IMarker marker : markersCache) {
if (marker.getResource().equals(file)) {
if (isMarkerOfSeverity(marker, IMarker.SEVERITY_ERROR)) {
return ERROR;
}
hasWarning |= isMarkerOfSeverity(marker, IMarker.SEVERITY_WARNING);
}
}
return hasWarning ? WARNING : null;
}
private void refreshMarkersCache(final IResource resource) {
try {
if (!resource.exists()) { // resource does not exists anymore
return;
}
final IMarker[] markers = resource.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
markersCache.clear();
for (final IMarker marker : markers) {
markersCache.add(marker);
}
} catch (final CoreException e) {
RedPlugin.logError("Unable to refresh markers for resource " + resource.getFullPath(), e);
}
}
@Override
public void addListener(final ILabelProviderListener listener) {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
@Override
public void dispose() {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
listeners.clear();
}
@Override
public boolean isLabelProperty(final Object element, final String property) {
return false;
}
@Override
public void removeListener(final ILabelProviderListener listener) {
listeners.remove(listener);
}
@Override
public void resourceChanged(final IResourceChangeEvent event) {
final IResourceDelta delta = event.getDelta();
if (delta != null) {
try {
final CollectAllResourcesVisitor visitor = new CollectAllResourcesVisitor();
delta.accept(visitor);
final List<IResource> list = visitor.getResources();
final LabelProviderChangedEvent labelProviderChangedEvent = new LabelProviderChangedEvent(this,
list.toArray());
for (final ILabelProviderListener listener : listeners) {
listener.labelProviderChanged(labelProviderChangedEvent);
}
} catch (final CoreException e) {
throw new IllegalStateException(e);
}
}
}
private boolean isMarkerOfSeverity(final IMarker marker, final int severity) {
try {
final Integer result = (Integer) marker.getAttribute(IMarker.SEVERITY);
return result != null && result.intValue() == severity;
} catch (final CoreException e) {
return false;
}
}
private boolean isDescendentOf(final IMarker marker, final IFolder folder) {
final IResource resource = marker.getResource();
if (resource != null && resource.exists()) {
final IPath resourcePath = resource.getLocation();
final IPath folderPath = folder.getLocation();
return folderPath.isPrefixOf(resourcePath);
}
return false;
}
private static class CollectAllResourcesVisitor implements IResourceDeltaVisitor {
private final List<IResource> resources = new ArrayList<IResource>();
@Override
public boolean visit(final IResourceDelta delta) {
resources.add(delta.getResource());
return true;
}
public List<IResource> getResources() {
return resources;
}
}
}