/******************************************************************************* * Copyright (c) 2000, 2003 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package net.sourceforge.phpdt.ui; import java.util.Iterator; import net.sourceforge.phpdt.core.ICompilationUnit; import net.sourceforge.phpdt.core.IJavaElement; import net.sourceforge.phpdt.core.ISourceRange; import net.sourceforge.phpdt.core.ISourceReference; import net.sourceforge.phpdt.core.JavaModelException; import net.sourceforge.phpdt.internal.ui.PHPUiImages; import net.sourceforge.phpdt.internal.ui.viewsupport.IProblemChangedListener; import net.sourceforge.phpdt.internal.ui.viewsupport.ImageDescriptorRegistry; import net.sourceforge.phpdt.internal.ui.viewsupport.ImageImageDescriptor; import net.sourceforge.phpeclipse.PHPeclipsePlugin; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.util.ListenerList; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.IDecoration; import org.eclipse.jface.viewers.ILabelDecorator; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ILightweightLabelDecorator; import org.eclipse.jface.viewers.LabelProviderChangedEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.texteditor.MarkerAnnotation; /** * LabelDecorator that decorates an element's image with error and warning overlays that * represent the severity of markers attached to the element's underlying resource. To see * a problem decoration for a marker, the marker needs to be a subtype of IMarker.PROBLEM. *

* Note: Only images for elements in Java projects are currently updated on marker changes. *

* * @since 2.0 */ public class ProblemsLabelDecorator implements ILabelDecorator, ILightweightLabelDecorator { /** * This is a special LabelProviderChangedEvent carring additional * information whether the event orgins from a maker change. *

* ProblemsLabelChangedEvents are only generated by * ProblemsLabelDecorators. *

*/ public static class ProblemsLabelChangedEvent extends LabelProviderChangedEvent { private boolean fMarkerChange; /** * Note: This constructor is for internal use only. Clients should not call this constructor. */ public ProblemsLabelChangedEvent(IBaseLabelProvider source, IResource[] changedResource, boolean isMarkerChange) { super(source, changedResource); fMarkerChange= isMarkerChange; } /** * Returns whether this event origins from marker changes. If false an annotation * model change is the origin. In this case viewers not displaying working copies can ignore these * events. * * @return if this event origins from a marker change. */ public boolean isMarkerChange() { return fMarkerChange; } } private static final int ERRORTICK_WARNING= JavaElementImageDescriptor.WARNING; private static final int ERRORTICK_ERROR= JavaElementImageDescriptor.ERROR; private ImageDescriptorRegistry fRegistry; private boolean fUseNewRegistry= false; private IProblemChangedListener fProblemChangedListener; private ListenerList fListeners; /** * Creates a new ProblemsLabelDecorator. */ public ProblemsLabelDecorator() { this(null); fUseNewRegistry= true; } /* * Creates decorator with a shared image registry. * * @param registry The registry to use or null to use the Java plugin's * image registry. */ /** * Note: This constructor is for internal use only. Clients should not call this constructor. */ public ProblemsLabelDecorator(ImageDescriptorRegistry registry) { fRegistry= registry; fProblemChangedListener= null; } private ImageDescriptorRegistry getRegistry() { if (fRegistry == null) { fRegistry= fUseNewRegistry ? new ImageDescriptorRegistry() : PHPeclipsePlugin.getImageDescriptorRegistry(); } return fRegistry; } /* (non-Javadoc) * @see ILabelDecorator#decorateText(String, Object) */ public String decorateText(String text, Object element) { return text; } /* (non-Javadoc) * @see ILabelDecorator#decorateImage(Image, Object) */ public Image decorateImage(Image image, Object obj) { int adornmentFlags= computeAdornmentFlags(obj); if (adornmentFlags != 0) { ImageDescriptor baseImage= new ImageImageDescriptor(image); Rectangle bounds= image.getBounds(); return getRegistry().get(new JavaElementImageDescriptor(baseImage, adornmentFlags, new Point(bounds.width, bounds.height))); } return image; } /** * Note: This method is for internal use only. Clients should not call this method. */ protected int computeAdornmentFlags(Object obj) { try { if (obj instanceof IJavaElement) { IJavaElement element= (IJavaElement) obj; int type= element.getElementType(); switch (type) { case IJavaElement.JAVA_PROJECT: case IJavaElement.PACKAGE_FRAGMENT_ROOT: return getErrorTicksFromMarkers(element.getResource(), IResource.DEPTH_INFINITE, null); case IJavaElement.PACKAGE_FRAGMENT: case IJavaElement.CLASS_FILE: return getErrorTicksFromMarkers(element.getResource(), IResource.DEPTH_ONE, null); case IJavaElement.COMPILATION_UNIT: case IJavaElement.PACKAGE_DECLARATION: case IJavaElement.IMPORT_DECLARATION: case IJavaElement.IMPORT_CONTAINER: case IJavaElement.TYPE: case IJavaElement.INITIALIZER: case IJavaElement.METHOD: case IJavaElement.FIELD: ICompilationUnit cu= (ICompilationUnit) element.getAncestor(IJavaElement.COMPILATION_UNIT); if (cu != null) { ISourceReference ref= (type == IJavaElement.COMPILATION_UNIT) ? null : (ISourceReference) element; // The assumption is that only source elements in compilation unit can have markers if (cu.isWorkingCopy()) { // working copy: look at annotation model return getErrorTicksFromWorkingCopy((ICompilationUnit) cu.getOriginalElement(), ref); } return getErrorTicksFromMarkers(cu.getResource(), IResource.DEPTH_ONE, ref); } break; default: } } else if (obj instanceof IResource) { return getErrorTicksFromMarkers((IResource) obj, IResource.DEPTH_INFINITE, null); } } catch (CoreException e) { if (e instanceof JavaModelException) { if (((JavaModelException) e).isDoesNotExist()) { return 0; } } PHPeclipsePlugin.log(e); } return 0; } private int getErrorTicksFromMarkers(IResource res, int depth, ISourceReference sourceElement) throws CoreException { if (res == null || !res.isAccessible()) { return 0; } int info= 0; IMarker[] markers= res.findMarkers(IMarker.PROBLEM, true, depth); if (markers != null) { for (int i= 0; i < markers.length && (info != ERRORTICK_ERROR); i++) { IMarker curr= markers[i]; if (sourceElement == null || isMarkerInRange(curr, sourceElement)) { int priority= curr.getAttribute(IMarker.SEVERITY, -1); if (priority == IMarker.SEVERITY_WARNING) { info= ERRORTICK_WARNING; } else if (priority == IMarker.SEVERITY_ERROR) { info= ERRORTICK_ERROR; } } } } return info; } private boolean isMarkerInRange(IMarker marker, ISourceReference sourceElement) throws CoreException { if (marker.isSubtypeOf(IMarker.TEXT)) { int pos= marker.getAttribute(IMarker.CHAR_START, -1); return isInside(pos, sourceElement); } return false; } private int getErrorTicksFromWorkingCopy(ICompilationUnit original, ISourceReference sourceElement) throws CoreException { int info= 0; FileEditorInput editorInput= new FileEditorInput((IFile) original.getResource()); IAnnotationModel model= PHPeclipsePlugin.getDefault().getCompilationUnitDocumentProvider().getAnnotationModel(editorInput); if (model != null) { Iterator iter= model.getAnnotationIterator(); while ((info != ERRORTICK_ERROR) && iter.hasNext()) { Annotation curr= (Annotation) iter.next(); IMarker marker= isAnnotationInRange(model, curr, sourceElement); if (marker != null) { int priority= marker.getAttribute(IMarker.SEVERITY, -1); if (priority == IMarker.SEVERITY_WARNING) { info= ERRORTICK_WARNING; } else if (priority == IMarker.SEVERITY_ERROR) { info= ERRORTICK_ERROR; } } } } return info; } private IMarker isAnnotationInRange(IAnnotationModel model, Annotation annot, ISourceReference sourceElement) throws CoreException { if (annot instanceof MarkerAnnotation) { IMarker marker= ((MarkerAnnotation) annot).getMarker(); if (marker.exists() && marker.isSubtypeOf(IMarker.PROBLEM)) { Position pos= model.getPosition(annot); if (sourceElement == null || isInside(pos.getOffset(), sourceElement)) { return marker; } } } return null; } /** * Tests if a position is inside the source range of an element. * @param pos Position to be tested. * @param sourceElement Source element (must be a IJavaElement) * @return boolean Return true if position is located inside the source element. * @throws CoreException Exception thrown if element range could not be accessed. * * @since 2.1 */ protected boolean isInside(int pos, ISourceReference sourceElement) throws CoreException { ISourceRange range= sourceElement.getSourceRange(); if (range != null) { int rangeOffset= range.getOffset(); return (rangeOffset <= pos && rangeOffset + range.getLength() > pos); } return false; } /* (non-Javadoc) * @see IBaseLabelProvider#dispose() */ public void dispose() { if (fProblemChangedListener != null) { PHPeclipsePlugin.getDefault().getProblemMarkerManager().removeListener(fProblemChangedListener); fProblemChangedListener= null; } if (fRegistry != null && fUseNewRegistry) { fRegistry.dispose(); } } /* (non-Javadoc) * @see IBaseLabelProvider#isLabelProperty(Object, String) */ public boolean isLabelProperty(Object element, String property) { return true; } /* (non-Javadoc) * @see IBaseLabelProvider#addListener(ILabelProviderListener) */ public void addListener(ILabelProviderListener listener) { if (fListeners == null) { fListeners= new ListenerList(); } fListeners.add(listener); if (fProblemChangedListener == null) { fProblemChangedListener= new IProblemChangedListener() { public void problemsChanged(IResource[] changedResources, boolean isMarkerChange) { fireProblemsChanged(changedResources, isMarkerChange); } }; PHPeclipsePlugin.getDefault().getProblemMarkerManager().addListener(fProblemChangedListener); } } /* (non-Javadoc) * @see IBaseLabelProvider#removeListener(ILabelProviderListener) */ public void removeListener(ILabelProviderListener listener) { if (fListeners != null) { fListeners.remove(listener); if (fListeners.isEmpty() && fProblemChangedListener != null) { PHPeclipsePlugin.getDefault().getProblemMarkerManager().removeListener(fProblemChangedListener); fProblemChangedListener= null; } } } private void fireProblemsChanged(IResource[] changedResources, boolean isMarkerChange) { if (fListeners != null && !fListeners.isEmpty()) { LabelProviderChangedEvent event= new ProblemsLabelChangedEvent(this, changedResources, isMarkerChange); Object[] listeners= fListeners.getListeners(); for (int i= 0; i < listeners.length; i++) { ((ILabelProviderListener) listeners[i]).labelProviderChanged(event); } } } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ILightweightLabelDecorator#decorate(java.lang.Object, org.eclipse.jface.viewers.IDecoration) */ public void decorate(Object element, IDecoration decoration) { int adornmentFlags= computeAdornmentFlags(element); if (adornmentFlags == ERRORTICK_ERROR) { decoration.addOverlay(PHPUiImages.DESC_OVR_ERROR); } else if (adornmentFlags == ERRORTICK_WARNING) { decoration.addOverlay(PHPUiImages.DESC_OVR_WARNING); } } }