2a48670a820ce1566be6d8d7b328a7e722fc6701
[phpeclipse.git] / net.sourceforge.phpeclipse.ui / src / net / sourceforge / phpdt / ui / ProblemsLabelDecorator.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.ui;
12
13 import java.util.Iterator;
14
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;
25 import net.sourceforge.phpeclipse.ui.WebUI;
26
27 import org.eclipse.core.resources.IFile;
28 import org.eclipse.core.resources.IMarker;
29 import org.eclipse.core.resources.IResource;
30 import org.eclipse.core.runtime.CoreException;
31 import org.eclipse.jface.resource.ImageDescriptor;
32 import org.eclipse.jface.text.Position;
33 import org.eclipse.jface.text.source.Annotation;
34 import org.eclipse.jface.text.source.IAnnotationModel;
35 import org.eclipse.jface.util.ListenerList;
36 import org.eclipse.jface.viewers.IBaseLabelProvider;
37 import org.eclipse.jface.viewers.IDecoration;
38 import org.eclipse.jface.viewers.ILabelDecorator;
39 import org.eclipse.jface.viewers.ILabelProviderListener;
40 import org.eclipse.jface.viewers.ILightweightLabelDecorator;
41 import org.eclipse.jface.viewers.LabelProviderChangedEvent;
42 import org.eclipse.swt.graphics.Image;
43 import org.eclipse.swt.graphics.Point;
44 import org.eclipse.swt.graphics.Rectangle;
45 import org.eclipse.ui.part.FileEditorInput;
46 import org.eclipse.ui.texteditor.MarkerAnnotation;
47
48 /**
49  * LabelDecorator that decorates an element's image with error and warning
50  * overlays that represent the severity of markers attached to the element's
51  * underlying resource. To see a problem decoration for a marker, the marker
52  * needs to be a subtype of <code>IMarker.PROBLEM</code>.
53  * <p>
54  * Note: Only images for elements in Java projects are currently updated on
55  * marker changes.
56  * </p>
57  * 
58  * @since 2.0
59  */
60 public class ProblemsLabelDecorator implements ILabelDecorator,
61                 ILightweightLabelDecorator {
62
63         /**
64          * This is a special <code>LabelProviderChangedEvent</code> carring
65          * additional information whether the event orgins from a maker change.
66          * <p>
67          * <code>ProblemsLabelChangedEvent</code>s are only generated by <code>
68          * ProblemsLabelDecorator</code>s.
69          * </p>
70          */
71         public static class ProblemsLabelChangedEvent extends
72                         LabelProviderChangedEvent {
73
74                 private boolean fMarkerChange;
75
76                 /**
77                  * Note: This constructor is for internal use only. Clients should not
78                  * call this constructor.
79                  */
80                 public ProblemsLabelChangedEvent(IBaseLabelProvider source,
81                                 IResource[] changedResource, boolean isMarkerChange) {
82                         super(source, changedResource);
83                         fMarkerChange = isMarkerChange;
84                 }
85
86                 /**
87                  * Returns whether this event origins from marker changes. If
88                  * <code>false</code> an annotation model change is the origin. In
89                  * this case viewers not displaying working copies can ignore these
90                  * events.
91                  * 
92                  * @return if this event origins from a marker change.
93                  */
94                 public boolean isMarkerChange() {
95                         return fMarkerChange;
96                 }
97
98         }
99
100         private static final int ERRORTICK_WARNING = JavaElementImageDescriptor.WARNING;
101
102         private static final int ERRORTICK_ERROR = JavaElementImageDescriptor.ERROR;
103
104         private ImageDescriptorRegistry fRegistry;
105
106         private boolean fUseNewRegistry = false;
107
108         private IProblemChangedListener fProblemChangedListener;
109
110         private ListenerList fListeners;
111
112         /**
113          * Creates a new <code>ProblemsLabelDecorator</code>.
114          */
115         public ProblemsLabelDecorator() {
116                 this(null);
117                 fUseNewRegistry = true;
118         }
119
120         /*
121          * Creates decorator with a shared image registry.
122          * 
123          * @param registry The registry to use or <code>null</code> to use the
124          * Java plugin's image registry.
125          */
126         /**
127          * Note: This constructor is for internal use only. Clients should not call
128          * this constructor.
129          */
130         public ProblemsLabelDecorator(ImageDescriptorRegistry registry) {
131                 fRegistry = registry;
132                 fProblemChangedListener = null;
133         }
134
135         private ImageDescriptorRegistry getRegistry() {
136                 if (fRegistry == null) {
137                         fRegistry = fUseNewRegistry ? new ImageDescriptorRegistry()
138                                         : WebUI.getImageDescriptorRegistry();
139                 }
140                 return fRegistry;
141         }
142
143         /*
144          * (non-Javadoc)
145          * 
146          * @see ILabelDecorator#decorateText(String, Object)
147          */
148         public String decorateText(String text, Object element) {
149                 return text;
150         }
151
152         /*
153          * (non-Javadoc)
154          * 
155          * @see ILabelDecorator#decorateImage(Image, Object)
156          */
157         public Image decorateImage(Image image, Object obj) {
158                 int adornmentFlags = computeAdornmentFlags(obj);
159                 if (adornmentFlags != 0) {
160                         ImageDescriptor baseImage = new ImageImageDescriptor(image);
161                         Rectangle bounds = image.getBounds();
162                         return getRegistry().get(
163                                         new JavaElementImageDescriptor(baseImage, adornmentFlags,
164                                                         new Point(bounds.width, bounds.height)));
165                 }
166                 return image;
167         }
168
169         /**
170          * Note: This method is for internal use only. Clients should not call this
171          * method.
172          */
173         protected int computeAdornmentFlags(Object obj) {
174                 try {
175                         if (obj instanceof IJavaElement) {
176                                 IJavaElement element = (IJavaElement) obj;
177                                 int type = element.getElementType();
178                                 switch (type) {
179                                 case IJavaElement.JAVA_PROJECT:
180                                 case IJavaElement.PACKAGE_FRAGMENT_ROOT:
181                                         return getErrorTicksFromMarkers(element.getResource(),
182                                                         IResource.DEPTH_INFINITE, null);
183                                 case IJavaElement.PACKAGE_FRAGMENT:
184                                 case IJavaElement.CLASS_FILE:
185                                         return getErrorTicksFromMarkers(element.getResource(),
186                                                         IResource.DEPTH_ONE, null);
187                                 case IJavaElement.COMPILATION_UNIT:
188                                 case IJavaElement.PACKAGE_DECLARATION:
189                                 case IJavaElement.IMPORT_DECLARATION:
190                                 case IJavaElement.IMPORT_CONTAINER:
191                                 case IJavaElement.TYPE:
192                                 case IJavaElement.INITIALIZER:
193                                 case IJavaElement.METHOD:
194                                 case IJavaElement.FIELD:
195                                         ICompilationUnit cu = (ICompilationUnit) element
196                                                         .getAncestor(IJavaElement.COMPILATION_UNIT);
197                                         if (cu != null) {
198                                                 ISourceReference ref = (type == IJavaElement.COMPILATION_UNIT) ? null
199                                                                 : (ISourceReference) element;
200                                                 // The assumption is that only source elements in
201                                                 // compilation unit can have markers
202                                                 if (cu.isWorkingCopy()) {
203                                                         // working copy: look at annotation model
204                                                         return getErrorTicksFromWorkingCopy(
205                                                                         (ICompilationUnit) cu.getOriginalElement(),
206                                                                         ref);
207                                                 }
208                                                 return getErrorTicksFromMarkers(cu.getResource(),
209                                                                 IResource.DEPTH_ONE, ref);
210                                         }
211                                         break;
212                                 default:
213                                 }
214                         } else if (obj instanceof IResource) {
215                                 return getErrorTicksFromMarkers((IResource) obj,
216                                                 IResource.DEPTH_INFINITE, null);
217                         }
218                 } catch (CoreException e) {
219                         if (e instanceof JavaModelException) {
220                                 if (((JavaModelException) e).isDoesNotExist()) {
221                                         return 0;
222                                 }
223                         }
224                         PHPeclipsePlugin.log(e);
225                 }
226                 return 0;
227         }
228
229         private int getErrorTicksFromMarkers(IResource res, int depth,
230                         ISourceReference sourceElement) throws CoreException {
231                 if (res == null || !res.isAccessible()) {
232                         return 0;
233                 }
234                 int info = 0;
235
236                 IMarker[] markers = res.findMarkers(IMarker.PROBLEM, true, depth);
237                 if (markers != null) {
238                         for (int i = 0; i < markers.length && (info != ERRORTICK_ERROR); i++) {
239                                 IMarker curr = markers[i];
240                                 if (sourceElement == null
241                                                 || isMarkerInRange(curr, sourceElement)) {
242                                         int priority = curr.getAttribute(IMarker.SEVERITY, -1);
243                                         if (priority == IMarker.SEVERITY_WARNING) {
244                                                 info = ERRORTICK_WARNING;
245                                         } else if (priority == IMarker.SEVERITY_ERROR) {
246                                                 info = ERRORTICK_ERROR;
247                                         }
248                                 }
249                         }
250                 }
251                 return info;
252         }
253
254         private boolean isMarkerInRange(IMarker marker,
255                         ISourceReference sourceElement) throws CoreException {
256                 if (marker.isSubtypeOf(IMarker.TEXT)) {
257                         int pos = marker.getAttribute(IMarker.CHAR_START, -1);
258                         return isInside(pos, sourceElement);
259                 }
260                 return false;
261         }
262
263         private int getErrorTicksFromWorkingCopy(ICompilationUnit original,
264                         ISourceReference sourceElement) throws CoreException {
265                 int info = 0;
266                 FileEditorInput editorInput = new FileEditorInput((IFile) original
267                                 .getResource());
268                 IAnnotationModel model = WebUI.getDefault()
269                                 .getCompilationUnitDocumentProvider().getAnnotationModel(
270                                                 editorInput);
271                 if (model != null) {
272                         Iterator iter = model.getAnnotationIterator();
273                         while ((info != ERRORTICK_ERROR) && iter.hasNext()) {
274                                 Annotation curr = (Annotation) iter.next();
275                                 IMarker marker = isAnnotationInRange(model, curr, sourceElement);
276                                 if (marker != null) {
277                                         int priority = marker.getAttribute(IMarker.SEVERITY, -1);
278                                         if (priority == IMarker.SEVERITY_WARNING) {
279                                                 info = ERRORTICK_WARNING;
280                                         } else if (priority == IMarker.SEVERITY_ERROR) {
281                                                 info = ERRORTICK_ERROR;
282                                         }
283                                 }
284                         }
285                 }
286                 return info;
287         }
288
289         private IMarker isAnnotationInRange(IAnnotationModel model,
290                         Annotation annot, ISourceReference sourceElement)
291                         throws CoreException {
292                 if (annot instanceof MarkerAnnotation) {
293                         IMarker marker = ((MarkerAnnotation) annot).getMarker();
294                         if (marker.exists() && marker.isSubtypeOf(IMarker.PROBLEM)) {
295                                 Position pos = model.getPosition(annot);
296                                 if (sourceElement == null
297                                                 || isInside(pos.getOffset(), sourceElement)) {
298                                         return marker;
299                                 }
300                         }
301                 }
302                 return null;
303         }
304
305         /**
306          * Tests if a position is inside the source range of an element.
307          * 
308          * @param pos
309          *            Position to be tested.
310          * @param sourceElement
311          *            Source element (must be a IJavaElement)
312          * @return boolean Return <code>true</code> if position is located inside
313          *         the source element.
314          * @throws CoreException
315          *             Exception thrown if element range could not be accessed.
316          * 
317          * @since 2.1
318          */
319         protected boolean isInside(int pos, ISourceReference sourceElement)
320                         throws CoreException {
321                 ISourceRange range = sourceElement.getSourceRange();
322                 if (range != null) {
323                         int rangeOffset = range.getOffset();
324                         return (rangeOffset <= pos && rangeOffset + range.getLength() > pos);
325                 }
326                 return false;
327         }
328
329         /*
330          * (non-Javadoc)
331          * 
332          * @see IBaseLabelProvider#dispose()
333          */
334         public void dispose() {
335                 if (fProblemChangedListener != null) {
336                         WebUI.getDefault().getProblemMarkerManager()
337                                         .removeListener(fProblemChangedListener);
338                         fProblemChangedListener = null;
339                 }
340                 if (fRegistry != null && fUseNewRegistry) {
341                         fRegistry.dispose();
342                 }
343         }
344
345         /*
346          * (non-Javadoc)
347          * 
348          * @see IBaseLabelProvider#isLabelProperty(Object, String)
349          */
350         public boolean isLabelProperty(Object element, String property) {
351                 return true;
352         }
353
354         /*
355          * (non-Javadoc)
356          * 
357          * @see IBaseLabelProvider#addListener(ILabelProviderListener)
358          */
359         public void addListener(ILabelProviderListener listener) {
360                 if (fListeners == null) {
361                         fListeners = new ListenerList();
362                 }
363                 fListeners.add(listener);
364                 if (fProblemChangedListener == null) {
365                         fProblemChangedListener = new IProblemChangedListener() {
366                                 public void problemsChanged(IResource[] changedResources,
367                                                 boolean isMarkerChange) {
368                                         fireProblemsChanged(changedResources, isMarkerChange);
369                                 }
370                         };
371                         WebUI.getDefault().getProblemMarkerManager()
372                                         .addListener(fProblemChangedListener);
373                 }
374         }
375
376         /*
377          * (non-Javadoc)
378          * 
379          * @see IBaseLabelProvider#removeListener(ILabelProviderListener)
380          */
381         public void removeListener(ILabelProviderListener listener) {
382                 if (fListeners != null) {
383                         fListeners.remove(listener);
384                         if (fListeners.isEmpty() && fProblemChangedListener != null) {
385                                 WebUI.getDefault().getProblemMarkerManager()
386                                                 .removeListener(fProblemChangedListener);
387                                 fProblemChangedListener = null;
388                         }
389                 }
390         }
391
392         private void fireProblemsChanged(IResource[] changedResources,
393                         boolean isMarkerChange) {
394                 if (fListeners != null && !fListeners.isEmpty()) {
395                         LabelProviderChangedEvent event = new ProblemsLabelChangedEvent(
396                                         this, changedResources, isMarkerChange);
397                         Object[] listeners = fListeners.getListeners();
398                         for (int i = 0; i < listeners.length; i++) {
399                                 ((ILabelProviderListener) listeners[i])
400                                                 .labelProviderChanged(event);
401                         }
402                 }
403         }
404
405         /*
406          * (non-Javadoc)
407          * 
408          * @see org.eclipse.jface.viewers.ILightweightLabelDecorator#decorate(java.lang.Object,
409          *      org.eclipse.jface.viewers.IDecoration)
410          */
411         public void decorate(Object element, IDecoration decoration) {
412                 int adornmentFlags = computeAdornmentFlags(element);
413                 if (adornmentFlags == ERRORTICK_ERROR) {
414                         decoration.addOverlay(PHPUiImages.DESC_OVR_ERROR);
415                 } else if (adornmentFlags == ERRORTICK_WARNING) {
416                         decoration.addOverlay(PHPUiImages.DESC_OVR_WARNING);
417                 }
418         }
419
420 }