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