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;
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;
35 //import org.eclipse.jface.util.ListenerList;
36 import org.eclipse.core.runtime.ListenerList;
37 import org.eclipse.jface.viewers.IBaseLabelProvider;
38 import org.eclipse.jface.viewers.IDecoration;
39 import org.eclipse.jface.viewers.ILabelDecorator;
40 import org.eclipse.jface.viewers.ILabelProviderListener;
41 import org.eclipse.jface.viewers.ILightweightLabelDecorator;
42 import org.eclipse.jface.viewers.LabelProviderChangedEvent;
43 import org.eclipse.swt.graphics.Image;
44 import org.eclipse.swt.graphics.Point;
45 import org.eclipse.swt.graphics.Rectangle;
46 import org.eclipse.ui.part.FileEditorInput;
47 import org.eclipse.ui.texteditor.MarkerAnnotation;
50 * LabelDecorator that decorates an element's image with error and warning
51 * overlays that represent the severity of markers attached to the element's
52 * underlying resource. To see a problem decoration for a marker, the marker
53 * needs to be a subtype of <code>IMarker.PROBLEM</code>.
55 * Note: Only images for elements in Java projects are currently updated on
61 public class ProblemsLabelDecorator implements ILabelDecorator,
62 ILightweightLabelDecorator {
65 * This is a special <code>LabelProviderChangedEvent</code> carring
66 * additional information whether the event orgins from a maker change.
68 * <code>ProblemsLabelChangedEvent</code>s are only generated by <code>
69 * ProblemsLabelDecorator</code>s.
72 public static class ProblemsLabelChangedEvent extends
73 LabelProviderChangedEvent {
78 private static final long serialVersionUID = -7557704732816971319L;
79 private boolean fMarkerChange;
82 * Note: This constructor is for internal use only. Clients should not
83 * call this constructor.
85 public ProblemsLabelChangedEvent(IBaseLabelProvider source,
86 IResource[] changedResource, boolean isMarkerChange) {
87 super(source, changedResource);
88 fMarkerChange = isMarkerChange;
92 * Returns whether this event origins from marker changes. If
93 * <code>false</code> an annotation model change is the origin. In
94 * this case viewers not displaying working copies can ignore these
97 * @return if this event origins from a marker change.
99 public boolean isMarkerChange() {
100 return fMarkerChange;
105 private static final int ERRORTICK_WARNING = JavaElementImageDescriptor.WARNING;
107 private static final int ERRORTICK_ERROR = JavaElementImageDescriptor.ERROR;
109 private ImageDescriptorRegistry fRegistry;
111 private boolean fUseNewRegistry = false;
113 private IProblemChangedListener fProblemChangedListener;
115 private ListenerList fListeners;
118 * Creates a new <code>ProblemsLabelDecorator</code>.
120 // public ProblemsLabelDecorator() {
122 // fUseNewRegistry = true;
126 * Creates decorator with a shared image registry.
128 * @param registry The registry to use or <code>null</code> to use the
129 * Java plugin's image registry.
132 * Note: This constructor is for internal use only. Clients should not call
135 public ProblemsLabelDecorator(ImageDescriptorRegistry registry) {
136 fRegistry = registry;
137 fProblemChangedListener = null;
140 private ImageDescriptorRegistry getRegistry() {
141 if (fRegistry == null) {
142 fRegistry = fUseNewRegistry ? new ImageDescriptorRegistry()
143 : PHPeclipsePlugin.getImageDescriptorRegistry();
151 * @see ILabelDecorator#decorateText(String, Object)
153 public String decorateText(String text, Object element) {
160 * @see ILabelDecorator#decorateImage(Image, Object)
162 public Image decorateImage(Image image, Object obj) {
163 int adornmentFlags = computeAdornmentFlags(obj);
164 if (adornmentFlags != 0) {
165 ImageDescriptor baseImage = new ImageImageDescriptor(image);
166 Rectangle bounds = image.getBounds();
167 return getRegistry().get(
168 new JavaElementImageDescriptor(baseImage, adornmentFlags,
169 new Point(bounds.width, bounds.height)));
175 * Note: This method is for internal use only. Clients should not call this
178 protected int computeAdornmentFlags(Object obj) {
180 if (obj instanceof IJavaElement) {
181 IJavaElement element = (IJavaElement) obj;
182 int type = element.getElementType();
184 case IJavaElement.JAVA_PROJECT:
185 case IJavaElement.PACKAGE_FRAGMENT_ROOT:
186 return getErrorTicksFromMarkers(element.getResource(),
187 IResource.DEPTH_INFINITE, null);
188 case IJavaElement.PACKAGE_FRAGMENT:
189 case IJavaElement.CLASS_FILE:
190 return getErrorTicksFromMarkers(element.getResource(),
191 IResource.DEPTH_ONE, null);
192 case IJavaElement.COMPILATION_UNIT:
193 case IJavaElement.PACKAGE_DECLARATION:
194 case IJavaElement.IMPORT_DECLARATION:
195 case IJavaElement.IMPORT_CONTAINER:
196 case IJavaElement.TYPE:
197 case IJavaElement.INITIALIZER:
198 case IJavaElement.METHOD:
199 case IJavaElement.FIELD:
200 ICompilationUnit cu = (ICompilationUnit) element
201 .getAncestor(IJavaElement.COMPILATION_UNIT);
203 ISourceReference ref = (type == IJavaElement.COMPILATION_UNIT) ? null
204 : (ISourceReference) element;
205 // The assumption is that only source elements in
206 // compilation unit can have markers
207 if (cu.isWorkingCopy()) {
208 // working copy: look at annotation model
209 return getErrorTicksFromWorkingCopy(
210 (ICompilationUnit) cu.getOriginalElement(),
213 return getErrorTicksFromMarkers(cu.getResource(),
214 IResource.DEPTH_ONE, ref);
219 } else if (obj instanceof IResource) {
220 return getErrorTicksFromMarkers((IResource) obj,
221 IResource.DEPTH_INFINITE, null);
223 } catch (CoreException e) {
224 if (e instanceof JavaModelException) {
225 if (((JavaModelException) e).isDoesNotExist()) {
229 PHPeclipsePlugin.log(e);
234 private int getErrorTicksFromMarkers(IResource res, int depth,
235 ISourceReference sourceElement) throws CoreException {
236 if (res == null || !res.isAccessible()) {
241 IMarker[] markers = res.findMarkers(IMarker.PROBLEM, true, depth);
242 if (markers != null) {
243 for (int i = 0; i < markers.length && (info != ERRORTICK_ERROR); i++) {
244 IMarker curr = markers[i];
245 if (sourceElement == null
246 || isMarkerInRange(curr, sourceElement)) {
247 int priority = curr.getAttribute(IMarker.SEVERITY, -1);
248 if (priority == IMarker.SEVERITY_WARNING) {
249 info = ERRORTICK_WARNING;
250 } else if (priority == IMarker.SEVERITY_ERROR) {
251 info = ERRORTICK_ERROR;
259 private boolean isMarkerInRange(IMarker marker,
260 ISourceReference sourceElement) throws CoreException {
261 if (marker.isSubtypeOf(IMarker.TEXT)) {
262 int pos = marker.getAttribute(IMarker.CHAR_START, -1);
263 return isInside(pos, sourceElement);
268 private int getErrorTicksFromWorkingCopy(ICompilationUnit original,
269 ISourceReference sourceElement) throws CoreException {
271 FileEditorInput editorInput = new FileEditorInput((IFile) original
273 IAnnotationModel model = PHPeclipsePlugin.getDefault()
274 .getCompilationUnitDocumentProvider().getAnnotationModel(
277 Iterator iter = model.getAnnotationIterator();
278 while ((info != ERRORTICK_ERROR) && iter.hasNext()) {
279 Annotation curr = (Annotation) iter.next();
280 IMarker marker = isAnnotationInRange(model, curr, sourceElement);
281 if (marker != null) {
282 int priority = marker.getAttribute(IMarker.SEVERITY, -1);
283 if (priority == IMarker.SEVERITY_WARNING) {
284 info = ERRORTICK_WARNING;
285 } else if (priority == IMarker.SEVERITY_ERROR) {
286 info = ERRORTICK_ERROR;
294 private IMarker isAnnotationInRange(IAnnotationModel model,
295 Annotation annot, ISourceReference sourceElement)
296 throws CoreException {
297 if (annot instanceof MarkerAnnotation) {
298 IMarker marker = ((MarkerAnnotation) annot).getMarker();
299 if (marker.exists() && marker.isSubtypeOf(IMarker.PROBLEM)) {
300 Position pos = model.getPosition(annot);
301 if (sourceElement == null
302 || isInside(pos.getOffset(), sourceElement)) {
311 * Tests if a position is inside the source range of an element.
314 * Position to be tested.
315 * @param sourceElement
316 * Source element (must be a IJavaElement)
317 * @return boolean Return <code>true</code> if position is located inside
318 * the source element.
319 * @throws CoreException
320 * Exception thrown if element range could not be accessed.
324 protected boolean isInside(int pos, ISourceReference sourceElement)
325 throws CoreException {
326 ISourceRange range = sourceElement.getSourceRange();
328 int rangeOffset = range.getOffset();
329 return (rangeOffset <= pos && rangeOffset + range.getLength() > pos);
337 * @see IBaseLabelProvider#dispose()
339 public void dispose() {
340 if (fProblemChangedListener != null) {
341 PHPeclipsePlugin.getDefault().getProblemMarkerManager()
342 .removeListener(fProblemChangedListener);
343 fProblemChangedListener = null;
345 if (fRegistry != null && fUseNewRegistry) {
353 * @see IBaseLabelProvider#isLabelProperty(Object, String)
355 public boolean isLabelProperty(Object element, String property) {
362 * @see IBaseLabelProvider#addListener(ILabelProviderListener)
364 public void addListener(ILabelProviderListener listener) {
365 if (fListeners == null) {
366 fListeners = new ListenerList();
368 fListeners.add(listener);
369 if (fProblemChangedListener == null) {
370 fProblemChangedListener = new IProblemChangedListener() {
371 public void problemsChanged(IResource[] changedResources,
372 boolean isMarkerChange) {
373 fireProblemsChanged(changedResources, isMarkerChange);
376 PHPeclipsePlugin.getDefault().getProblemMarkerManager()
377 .addListener(fProblemChangedListener);
384 * @see IBaseLabelProvider#removeListener(ILabelProviderListener)
386 public void removeListener(ILabelProviderListener listener) {
387 if (fListeners != null) {
388 fListeners.remove(listener);
389 if (fListeners.isEmpty() && fProblemChangedListener != null) {
390 PHPeclipsePlugin.getDefault().getProblemMarkerManager()
391 .removeListener(fProblemChangedListener);
392 fProblemChangedListener = null;
397 private void fireProblemsChanged(IResource[] changedResources,
398 boolean isMarkerChange) {
399 if (fListeners != null && !fListeners.isEmpty()) {
400 LabelProviderChangedEvent event = new ProblemsLabelChangedEvent(
401 this, changedResources, isMarkerChange);
402 Object[] listeners = fListeners.getListeners();
403 for (int i = 0; i < listeners.length; i++) {
404 ((ILabelProviderListener) listeners[i])
405 .labelProviderChanged(event);
413 * @see org.eclipse.jface.viewers.ILightweightLabelDecorator#decorate(java.lang.Object,
414 * org.eclipse.jface.viewers.IDecoration)
416 public void decorate(Object element, IDecoration decoration) {
417 int adornmentFlags = computeAdornmentFlags(element);
418 if (adornmentFlags == ERRORTICK_ERROR) {
419 decoration.addOverlay(PHPUiImages.DESC_OVR_ERROR);
420 } else if (adornmentFlags == ERRORTICK_WARNING) {
421 decoration.addOverlay(PHPUiImages.DESC_OVR_WARNING);