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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.ui;
13 import java.util.Iterator;
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;
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;
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>.
54 * Note: Only images for elements in Java projects are currently updated on
60 public class ProblemsLabelDecorator implements ILabelDecorator,
61 ILightweightLabelDecorator {
64 * This is a special <code>LabelProviderChangedEvent</code> carring
65 * additional information whether the event orgins from a maker change.
67 * <code>ProblemsLabelChangedEvent</code>s are only generated by <code>
68 * ProblemsLabelDecorator</code>s.
71 public static class ProblemsLabelChangedEvent extends
72 LabelProviderChangedEvent {
74 private boolean fMarkerChange;
77 * Note: This constructor is for internal use only. Clients should not
78 * call this constructor.
80 public ProblemsLabelChangedEvent(IBaseLabelProvider source,
81 IResource[] changedResource, boolean isMarkerChange) {
82 super(source, changedResource);
83 fMarkerChange = isMarkerChange;
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
92 * @return if this event origins from a marker change.
94 public boolean isMarkerChange() {
100 private static final int ERRORTICK_WARNING = JavaElementImageDescriptor.WARNING;
102 private static final int ERRORTICK_ERROR = JavaElementImageDescriptor.ERROR;
104 private ImageDescriptorRegistry fRegistry;
106 private boolean fUseNewRegistry = false;
108 private IProblemChangedListener fProblemChangedListener;
110 private ListenerList fListeners;
113 * Creates a new <code>ProblemsLabelDecorator</code>.
115 public ProblemsLabelDecorator() {
117 fUseNewRegistry = true;
121 * Creates decorator with a shared image registry.
123 * @param registry The registry to use or <code>null</code> to use the
124 * Java plugin's image registry.
127 * Note: This constructor is for internal use only. Clients should not call
130 public ProblemsLabelDecorator(ImageDescriptorRegistry registry) {
131 fRegistry = registry;
132 fProblemChangedListener = null;
135 private ImageDescriptorRegistry getRegistry() {
136 if (fRegistry == null) {
137 fRegistry = fUseNewRegistry ? new ImageDescriptorRegistry()
138 : WebUI.getImageDescriptorRegistry();
146 * @see ILabelDecorator#decorateText(String, Object)
148 public String decorateText(String text, Object element) {
155 * @see ILabelDecorator#decorateImage(Image, Object)
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)));
170 * Note: This method is for internal use only. Clients should not call this
173 protected int computeAdornmentFlags(Object obj) {
175 if (obj instanceof IJavaElement) {
176 IJavaElement element = (IJavaElement) obj;
177 int type = element.getElementType();
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);
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(),
208 return getErrorTicksFromMarkers(cu.getResource(),
209 IResource.DEPTH_ONE, ref);
214 } else if (obj instanceof IResource) {
215 return getErrorTicksFromMarkers((IResource) obj,
216 IResource.DEPTH_INFINITE, null);
218 } catch (CoreException e) {
219 if (e instanceof JavaModelException) {
220 if (((JavaModelException) e).isDoesNotExist()) {
224 PHPeclipsePlugin.log(e);
229 private int getErrorTicksFromMarkers(IResource res, int depth,
230 ISourceReference sourceElement) throws CoreException {
231 if (res == null || !res.isAccessible()) {
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;
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);
263 private int getErrorTicksFromWorkingCopy(ICompilationUnit original,
264 ISourceReference sourceElement) throws CoreException {
266 FileEditorInput editorInput = new FileEditorInput((IFile) original
268 IAnnotationModel model = WebUI.getDefault()
269 .getCompilationUnitDocumentProvider().getAnnotationModel(
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;
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)) {
306 * Tests if a position is inside the source range of an element.
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.
319 protected boolean isInside(int pos, ISourceReference sourceElement)
320 throws CoreException {
321 ISourceRange range = sourceElement.getSourceRange();
323 int rangeOffset = range.getOffset();
324 return (rangeOffset <= pos && rangeOffset + range.getLength() > pos);
332 * @see IBaseLabelProvider#dispose()
334 public void dispose() {
335 if (fProblemChangedListener != null) {
336 WebUI.getDefault().getProblemMarkerManager()
337 .removeListener(fProblemChangedListener);
338 fProblemChangedListener = null;
340 if (fRegistry != null && fUseNewRegistry) {
348 * @see IBaseLabelProvider#isLabelProperty(Object, String)
350 public boolean isLabelProperty(Object element, String property) {
357 * @see IBaseLabelProvider#addListener(ILabelProviderListener)
359 public void addListener(ILabelProviderListener listener) {
360 if (fListeners == null) {
361 fListeners = new ListenerList();
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);
371 WebUI.getDefault().getProblemMarkerManager()
372 .addListener(fProblemChangedListener);
379 * @see IBaseLabelProvider#removeListener(ILabelProviderListener)
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;
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);
408 * @see org.eclipse.jface.viewers.ILightweightLabelDecorator#decorate(java.lang.Object,
409 * org.eclipse.jface.viewers.IDecoration)
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);