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