new version with WorkingCopy Management
[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 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>.
51  * <p>
52  * Note: Only images for elements in Java projects are currently updated on marker changes.
53  * </p>
54  * 
55  * @since 2.0
56  */
57 public class ProblemsLabelDecorator implements ILabelDecorator, ILightweightLabelDecorator {
58         
59         /**
60          * This is a special <code>LabelProviderChangedEvent</code> carring additional 
61          * information whether the event orgins from a maker change.
62          * <p>
63          * <code>ProblemsLabelChangedEvent</code>s are only generated by <code>
64          * ProblemsLabelDecorator</code>s.
65          * </p>
66          */
67         public static class ProblemsLabelChangedEvent extends LabelProviderChangedEvent {
68
69                 private boolean fMarkerChange;
70
71                 /**
72                  * Note: This constructor is for internal use only. Clients should not call this constructor.
73                  */
74                 public ProblemsLabelChangedEvent(IBaseLabelProvider source, IResource[] changedResource, boolean isMarkerChange) {
75                         super(source, changedResource);
76                         fMarkerChange= isMarkerChange;
77                 }
78                 
79                 /**
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 
82                  * events.
83                  * 
84                  * @return if this event origins from a marker change.
85                  */
86                 public boolean isMarkerChange() {
87                         return fMarkerChange;
88                 }
89
90         }
91
92         private static final int ERRORTICK_WARNING= JavaElementImageDescriptor.WARNING;
93         private static final int ERRORTICK_ERROR= JavaElementImageDescriptor.ERROR;     
94
95         private ImageDescriptorRegistry fRegistry;
96         private boolean fUseNewRegistry= false;
97         private IProblemChangedListener fProblemChangedListener;
98         
99         private ListenerList fListeners;
100
101         /**
102          * Creates a new <code>ProblemsLabelDecorator</code>.
103          */
104         public ProblemsLabelDecorator() {
105                 this(null);
106                 fUseNewRegistry= true;
107         }
108         
109         /*
110          * Creates decorator with a shared image registry.
111          * 
112          * @param registry The registry to use or <code>null</code> to use the Java plugin's
113          * image registry.
114          */
115         /**
116          * Note: This constructor is for internal use only. Clients should not call this constructor.
117          */
118         public ProblemsLabelDecorator(ImageDescriptorRegistry registry) {
119                 fRegistry= registry;
120                 fProblemChangedListener= null;
121         }
122         
123         private ImageDescriptorRegistry getRegistry() {
124                 if (fRegistry == null) {
125                         fRegistry= fUseNewRegistry ? new ImageDescriptorRegistry() : PHPeclipsePlugin.getImageDescriptorRegistry();
126                 }
127                 return fRegistry;
128         }
129         
130
131         /* (non-Javadoc)
132          * @see ILabelDecorator#decorateText(String, Object)
133          */
134         public String decorateText(String text, Object element) {
135                 return text;
136         }       
137
138         /* (non-Javadoc)
139          * @see ILabelDecorator#decorateImage(Image, Object)
140          */
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)));
147                 }
148                 return image;
149         }
150
151         /**
152          * Note: This method is for internal use only. Clients should not call this method.
153          */
154         protected int computeAdornmentFlags(Object obj) {
155                 try {
156                         if (obj instanceof IJavaElement) {
157                                 IJavaElement element= (IJavaElement) obj;
158                                 int type= element.getElementType();
159                                 switch (type) {
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);
175                                                 if (cu != null) {
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);
181                                                         }
182                                                         return getErrorTicksFromMarkers(cu.getResource(), IResource.DEPTH_ONE, ref);
183                                                 }
184                                                 break;
185                                         default:
186                                 }
187                         } else if (obj instanceof IResource) {
188                                 return getErrorTicksFromMarkers((IResource) obj, IResource.DEPTH_INFINITE, null);
189                         }
190                 } catch (CoreException e) {
191                         if (e instanceof JavaModelException) {
192                                 if (((JavaModelException) e).isDoesNotExist()) {
193                                         return 0;
194                                 }
195                         }
196                         PHPeclipsePlugin.log(e);
197                 }
198                 return 0;
199         }
200
201         private int getErrorTicksFromMarkers(IResource res, int depth, ISourceReference sourceElement) throws CoreException {
202                 if (res == null || !res.isAccessible()) {
203                         return 0;
204                 }
205                 int info= 0;
206                 
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;
217                                         }
218                                 }
219                         }                       
220                 }
221                 return info;
222         }
223
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);
228                 }
229                 return false;
230         }
231         
232         
233         private int getErrorTicksFromWorkingCopy(ICompilationUnit original, ISourceReference sourceElement) throws CoreException {
234                 int info= 0;
235                 FileEditorInput editorInput= new FileEditorInput((IFile) original.getResource());
236                 IAnnotationModel model= PHPeclipsePlugin.getDefault().getCompilationUnitDocumentProvider().getAnnotationModel(editorInput);
237                 if (model != null) {
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;
248                                         }
249                                 }
250                         }
251                 }
252                 return info;
253         }
254                         
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)) {
261                                         return marker;
262                                 }
263                         }
264                 }
265                 return null;
266         }
267         
268         /**
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.
274          * 
275          * @since 2.1
276          */
277         protected boolean isInside(int pos, ISourceReference sourceElement) throws CoreException {
278                 ISourceRange range= sourceElement.getSourceRange();
279                 if (range != null) {
280                         int rangeOffset= range.getOffset();
281                         return (rangeOffset <= pos && rangeOffset + range.getLength() > pos);                   
282                 }
283                 return false;
284         }       
285         
286         /* (non-Javadoc)
287          * @see IBaseLabelProvider#dispose()
288          */
289         public void dispose() {
290                 if (fProblemChangedListener != null) {
291                         PHPeclipsePlugin.getDefault().getProblemMarkerManager().removeListener(fProblemChangedListener);
292                         fProblemChangedListener= null;
293                 }
294                 if (fRegistry != null && fUseNewRegistry) {
295                         fRegistry.dispose();
296                 }
297         }
298
299         /* (non-Javadoc)
300          * @see IBaseLabelProvider#isLabelProperty(Object, String)
301          */
302         public boolean isLabelProperty(Object element, String property) {
303                 return true;
304         }
305         
306         /* (non-Javadoc)
307          * @see IBaseLabelProvider#addListener(ILabelProviderListener)
308          */
309         public void addListener(ILabelProviderListener listener) {
310                 if (fListeners == null) {
311                         fListeners= new ListenerList();
312                 }
313                 fListeners.add(listener);
314                 if (fProblemChangedListener == null) {
315                         fProblemChangedListener= new IProblemChangedListener() {
316                                 public void problemsChanged(IResource[] changedResources, boolean isMarkerChange) {
317                                         fireProblemsChanged(changedResources, isMarkerChange);
318                                 }
319                         };
320                         PHPeclipsePlugin.getDefault().getProblemMarkerManager().addListener(fProblemChangedListener);
321                 }
322         }       
323
324         /* (non-Javadoc)
325          * @see IBaseLabelProvider#removeListener(ILabelProviderListener)
326          */
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;
333                         }
334                 }
335         }
336         
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);
343                         }
344                 }
345         }
346                 
347         /* (non-Javadoc)
348          * @see org.eclipse.jface.viewers.ILightweightLabelDecorator#decorate(java.lang.Object, org.eclipse.jface.viewers.IDecoration)
349          */
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);
356                 }               
357         }
358
359 }