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