1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.ui;
13 import java.util.Iterator;
15 import net.sourceforge.phpdt.core.ICompilationUnit;
16 import net.sourceforge.phpdt.core.IJavaElement;
17 import net.sourceforge.phpdt.core.ISourceRange;
18 import net.sourceforge.phpdt.core.ISourceReference;
19 import net.sourceforge.phpdt.core.JavaModelException;
20 import net.sourceforge.phpdt.internal.ui.PHPUiImages;
21 import net.sourceforge.phpdt.internal.ui.viewsupport.IProblemChangedListener;
22 import net.sourceforge.phpdt.internal.ui.viewsupport.ImageDescriptorRegistry;
23 import net.sourceforge.phpdt.internal.ui.viewsupport.ImageImageDescriptor;
24 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
26 import org.eclipse.core.resources.IFile;
27 import org.eclipse.core.resources.IMarker;
28 import org.eclipse.core.resources.IResource;
29 import org.eclipse.core.runtime.CoreException;
30 import org.eclipse.jface.resource.ImageDescriptor;
31 import org.eclipse.jface.text.Position;
32 import org.eclipse.jface.text.source.Annotation;
33 import org.eclipse.jface.text.source.IAnnotationModel;
34 import org.eclipse.jface.util.ListenerList;
35 import org.eclipse.jface.viewers.IBaseLabelProvider;
36 import org.eclipse.jface.viewers.IDecoration;
37 import org.eclipse.jface.viewers.ILabelDecorator;
38 import org.eclipse.jface.viewers.ILabelProviderListener;
39 import org.eclipse.jface.viewers.ILightweightLabelDecorator;
40 import org.eclipse.jface.viewers.LabelProviderChangedEvent;
41 import org.eclipse.swt.graphics.Image;
42 import org.eclipse.swt.graphics.Point;
43 import org.eclipse.swt.graphics.Rectangle;
44 import org.eclipse.ui.part.FileEditorInput;
45 import org.eclipse.ui.texteditor.MarkerAnnotation;
48 * LabelDecorator that decorates an element's image with error and warning overlays that
49 * represent the severity of markers attached to the element's underlying resource. To see
50 * a problem decoration for a marker, the marker needs to be a subtype of <code>IMarker.PROBLEM</code>.
52 * Note: Only images for elements in Java projects are currently updated on marker changes.
57 public class ProblemsLabelDecorator implements ILabelDecorator, ILightweightLabelDecorator {
60 * This is a special <code>LabelProviderChangedEvent</code> carring additional
61 * information whether the event orgins from a maker change.
63 * <code>ProblemsLabelChangedEvent</code>s are only generated by <code>
64 * ProblemsLabelDecorator</code>s.
67 public static class ProblemsLabelChangedEvent extends LabelProviderChangedEvent {
69 private boolean fMarkerChange;
72 * Note: This constructor is for internal use only. Clients should not call this constructor.
74 public ProblemsLabelChangedEvent(IBaseLabelProvider source, IResource[] changedResource, boolean isMarkerChange) {
75 super(source, changedResource);
76 fMarkerChange= isMarkerChange;
80 * Returns whether this event origins from marker changes. If <code>false</code> an annotation
81 * model change is the origin. In this case viewers not displaying working copies can ignore these
84 * @return if this event origins from a marker change.
86 public boolean isMarkerChange() {
92 private static final int ERRORTICK_WARNING= JavaElementImageDescriptor.WARNING;
93 private static final int ERRORTICK_ERROR= JavaElementImageDescriptor.ERROR;
95 private ImageDescriptorRegistry fRegistry;
96 private boolean fUseNewRegistry= false;
97 private IProblemChangedListener fProblemChangedListener;
99 private ListenerList fListeners;
102 * Creates a new <code>ProblemsLabelDecorator</code>.
104 public ProblemsLabelDecorator() {
106 fUseNewRegistry= true;
110 * Creates decorator with a shared image registry.
112 * @param registry The registry to use or <code>null</code> to use the Java plugin's
116 * Note: This constructor is for internal use only. Clients should not call this constructor.
118 public ProblemsLabelDecorator(ImageDescriptorRegistry registry) {
120 fProblemChangedListener= null;
123 private ImageDescriptorRegistry getRegistry() {
124 if (fRegistry == null) {
125 fRegistry= fUseNewRegistry ? new ImageDescriptorRegistry() : PHPeclipsePlugin.getImageDescriptorRegistry();
132 * @see ILabelDecorator#decorateText(String, Object)
134 public String decorateText(String text, Object element) {
139 * @see ILabelDecorator#decorateImage(Image, Object)
141 public Image decorateImage(Image image, Object obj) {
142 int adornmentFlags= computeAdornmentFlags(obj);
143 if (adornmentFlags != 0) {
144 ImageDescriptor baseImage= new ImageImageDescriptor(image);
145 Rectangle bounds= image.getBounds();
146 return getRegistry().get(new JavaElementImageDescriptor(baseImage, adornmentFlags, new Point(bounds.width, bounds.height)));
152 * Note: This method is for internal use only. Clients should not call this method.
154 protected int computeAdornmentFlags(Object obj) {
156 if (obj instanceof IJavaElement) {
157 IJavaElement element= (IJavaElement) obj;
158 int type= element.getElementType();
160 case IJavaElement.JAVA_PROJECT:
161 case IJavaElement.PACKAGE_FRAGMENT_ROOT:
162 return getErrorTicksFromMarkers(element.getResource(), IResource.DEPTH_INFINITE, null);
163 case IJavaElement.PACKAGE_FRAGMENT:
164 case IJavaElement.CLASS_FILE:
165 return getErrorTicksFromMarkers(element.getResource(), IResource.DEPTH_ONE, null);
166 case IJavaElement.COMPILATION_UNIT:
167 case IJavaElement.PACKAGE_DECLARATION:
168 case IJavaElement.IMPORT_DECLARATION:
169 case IJavaElement.IMPORT_CONTAINER:
170 case IJavaElement.TYPE:
171 case IJavaElement.INITIALIZER:
172 case IJavaElement.METHOD:
173 case IJavaElement.FIELD:
174 ICompilationUnit cu= (ICompilationUnit) element.getAncestor(IJavaElement.COMPILATION_UNIT);
176 ISourceReference ref= (type == IJavaElement.COMPILATION_UNIT) ? null : (ISourceReference) element;
177 // The assumption is that only source elements in compilation unit can have markers
178 if (cu.isWorkingCopy()) {
179 // working copy: look at annotation model
180 return getErrorTicksFromWorkingCopy((ICompilationUnit) cu.getOriginalElement(), ref);
182 return getErrorTicksFromMarkers(cu.getResource(), IResource.DEPTH_ONE, ref);
187 } else if (obj instanceof IResource) {
188 return getErrorTicksFromMarkers((IResource) obj, IResource.DEPTH_INFINITE, null);
190 } catch (CoreException e) {
191 if (e instanceof JavaModelException) {
192 if (((JavaModelException) e).isDoesNotExist()) {
196 PHPeclipsePlugin.log(e);
201 private int getErrorTicksFromMarkers(IResource res, int depth, ISourceReference sourceElement) throws CoreException {
202 if (res == null || !res.isAccessible()) {
207 IMarker[] markers= res.findMarkers(IMarker.PROBLEM, true, depth);
208 if (markers != null) {
209 for (int i= 0; i < markers.length && (info != ERRORTICK_ERROR); i++) {
210 IMarker curr= markers[i];
211 if (sourceElement == null || isMarkerInRange(curr, sourceElement)) {
212 int priority= curr.getAttribute(IMarker.SEVERITY, -1);
213 if (priority == IMarker.SEVERITY_WARNING) {
214 info= ERRORTICK_WARNING;
215 } else if (priority == IMarker.SEVERITY_ERROR) {
216 info= ERRORTICK_ERROR;
224 private boolean isMarkerInRange(IMarker marker, ISourceReference sourceElement) throws CoreException {
225 if (marker.isSubtypeOf(IMarker.TEXT)) {
226 int pos= marker.getAttribute(IMarker.CHAR_START, -1);
227 return isInside(pos, sourceElement);
233 private int getErrorTicksFromWorkingCopy(ICompilationUnit original, ISourceReference sourceElement) throws CoreException {
235 FileEditorInput editorInput= new FileEditorInput((IFile) original.getResource());
236 IAnnotationModel model= PHPeclipsePlugin.getDefault().getCompilationUnitDocumentProvider().getAnnotationModel(editorInput);
238 Iterator iter= model.getAnnotationIterator();
239 while ((info != ERRORTICK_ERROR) && iter.hasNext()) {
240 Annotation curr= (Annotation) iter.next();
241 IMarker marker= isAnnotationInRange(model, curr, sourceElement);
242 if (marker != null) {
243 int priority= marker.getAttribute(IMarker.SEVERITY, -1);
244 if (priority == IMarker.SEVERITY_WARNING) {
245 info= ERRORTICK_WARNING;
246 } else if (priority == IMarker.SEVERITY_ERROR) {
247 info= ERRORTICK_ERROR;
255 private IMarker isAnnotationInRange(IAnnotationModel model, Annotation annot, ISourceReference sourceElement) throws CoreException {
256 if (annot instanceof MarkerAnnotation) {
257 IMarker marker= ((MarkerAnnotation) annot).getMarker();
258 if (marker.exists() && marker.isSubtypeOf(IMarker.PROBLEM)) {
259 Position pos= model.getPosition(annot);
260 if (sourceElement == null || isInside(pos.getOffset(), sourceElement)) {
269 * Tests if a position is inside the source range of an element.
270 * @param pos Position to be tested.
271 * @param sourceElement Source element (must be a IJavaElement)
272 * @return boolean Return <code>true</code> if position is located inside the source element.
273 * @throws CoreException Exception thrown if element range could not be accessed.
277 protected boolean isInside(int pos, ISourceReference sourceElement) throws CoreException {
278 ISourceRange range= sourceElement.getSourceRange();
280 int rangeOffset= range.getOffset();
281 return (rangeOffset <= pos && rangeOffset + range.getLength() > pos);
287 * @see IBaseLabelProvider#dispose()
289 public void dispose() {
290 if (fProblemChangedListener != null) {
291 PHPeclipsePlugin.getDefault().getProblemMarkerManager().removeListener(fProblemChangedListener);
292 fProblemChangedListener= null;
294 if (fRegistry != null && fUseNewRegistry) {
300 * @see IBaseLabelProvider#isLabelProperty(Object, String)
302 public boolean isLabelProperty(Object element, String property) {
307 * @see IBaseLabelProvider#addListener(ILabelProviderListener)
309 public void addListener(ILabelProviderListener listener) {
310 if (fListeners == null) {
311 fListeners= new ListenerList();
313 fListeners.add(listener);
314 if (fProblemChangedListener == null) {
315 fProblemChangedListener= new IProblemChangedListener() {
316 public void problemsChanged(IResource[] changedResources, boolean isMarkerChange) {
317 fireProblemsChanged(changedResources, isMarkerChange);
320 PHPeclipsePlugin.getDefault().getProblemMarkerManager().addListener(fProblemChangedListener);
325 * @see IBaseLabelProvider#removeListener(ILabelProviderListener)
327 public void removeListener(ILabelProviderListener listener) {
328 if (fListeners != null) {
329 fListeners.remove(listener);
330 if (fListeners.isEmpty() && fProblemChangedListener != null) {
331 PHPeclipsePlugin.getDefault().getProblemMarkerManager().removeListener(fProblemChangedListener);
332 fProblemChangedListener= null;
337 private void fireProblemsChanged(IResource[] changedResources, boolean isMarkerChange) {
338 if (fListeners != null && !fListeners.isEmpty()) {
339 LabelProviderChangedEvent event= new ProblemsLabelChangedEvent(this, changedResources, isMarkerChange);
340 Object[] listeners= fListeners.getListeners();
341 for (int i= 0; i < listeners.length; i++) {
342 ((ILabelProviderListener) listeners[i]).labelProviderChanged(event);
348 * @see org.eclipse.jface.viewers.ILightweightLabelDecorator#decorate(java.lang.Object, org.eclipse.jface.viewers.IDecoration)
350 public void decorate(Object element, IDecoration decoration) {
351 int adornmentFlags= computeAdornmentFlags(element);
352 if (adornmentFlags == ERRORTICK_ERROR) {
353 decoration.addOverlay(PHPUiImages.DESC_OVR_ERROR);
354 } else if (adornmentFlags == ERRORTICK_WARNING) {
355 decoration.addOverlay(PHPUiImages.DESC_OVR_WARNING);