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;
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;
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>.
53 * Note: Only images for elements in Java projects are currently updated on
59 public class ProblemsLabelDecorator implements ILabelDecorator,
60 ILightweightLabelDecorator {
63 * This is a special <code>LabelProviderChangedEvent</code> carring
64 * additional information whether the event orgins from a maker change.
66 * <code>ProblemsLabelChangedEvent</code>s are only generated by <code>
67 * ProblemsLabelDecorator</code>s.
70 public static class ProblemsLabelChangedEvent extends
71 LabelProviderChangedEvent {
73 private boolean fMarkerChange;
76 * Note: This constructor is for internal use only. Clients should not
77 * call this constructor.
79 public ProblemsLabelChangedEvent(IBaseLabelProvider source,
80 IResource[] changedResource, boolean isMarkerChange) {
81 super(source, changedResource);
82 fMarkerChange = isMarkerChange;
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
91 * @return if this event origins from a marker change.
93 public boolean isMarkerChange() {
99 private static final int ERRORTICK_WARNING = JavaElementImageDescriptor.WARNING;
101 private static final int ERRORTICK_ERROR = JavaElementImageDescriptor.ERROR;
103 private ImageDescriptorRegistry fRegistry;
105 private boolean fUseNewRegistry = false;
107 private IProblemChangedListener fProblemChangedListener;
109 private ListenerList fListeners;
112 * Creates a new <code>ProblemsLabelDecorator</code>.
114 public ProblemsLabelDecorator() {
116 fUseNewRegistry = true;
120 * Creates decorator with a shared image registry.
122 * @param registry The registry to use or <code>null</code> to use the
123 * Java plugin's image registry.
126 * Note: This constructor is for internal use only. Clients should not call
129 public ProblemsLabelDecorator(ImageDescriptorRegistry registry) {
130 fRegistry = registry;
131 fProblemChangedListener = null;
134 private ImageDescriptorRegistry getRegistry() {
135 if (fRegistry == null) {
136 fRegistry = fUseNewRegistry ? new ImageDescriptorRegistry()
137 : PHPeclipsePlugin.getImageDescriptorRegistry();
145 * @see ILabelDecorator#decorateText(String, Object)
147 public String decorateText(String text, Object element) {
154 * @see ILabelDecorator#decorateImage(Image, Object)
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)));
169 * Note: This method is for internal use only. Clients should not call this
172 protected int computeAdornmentFlags(Object obj) {
174 if (obj instanceof IJavaElement) {
175 IJavaElement element = (IJavaElement) obj;
176 int type = element.getElementType();
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);
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(),
207 return getErrorTicksFromMarkers(cu.getResource(),
208 IResource.DEPTH_ONE, ref);
213 } else if (obj instanceof IResource) {
214 return getErrorTicksFromMarkers((IResource) obj,
215 IResource.DEPTH_INFINITE, null);
217 } catch (CoreException e) {
218 if (e instanceof JavaModelException) {
219 if (((JavaModelException) e).isDoesNotExist()) {
223 PHPeclipsePlugin.log(e);
228 private int getErrorTicksFromMarkers(IResource res, int depth,
229 ISourceReference sourceElement) throws CoreException {
230 if (res == null || !res.isAccessible()) {
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;
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);
262 private int getErrorTicksFromWorkingCopy(ICompilationUnit original,
263 ISourceReference sourceElement) throws CoreException {
265 FileEditorInput editorInput = new FileEditorInput((IFile) original
267 IAnnotationModel model = PHPeclipsePlugin.getDefault()
268 .getCompilationUnitDocumentProvider().getAnnotationModel(
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;
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)) {
305 * Tests if a position is inside the source range of an element.
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.
318 protected boolean isInside(int pos, ISourceReference sourceElement)
319 throws CoreException {
320 ISourceRange range = sourceElement.getSourceRange();
322 int rangeOffset = range.getOffset();
323 return (rangeOffset <= pos && rangeOffset + range.getLength() > pos);
331 * @see IBaseLabelProvider#dispose()
333 public void dispose() {
334 if (fProblemChangedListener != null) {
335 PHPeclipsePlugin.getDefault().getProblemMarkerManager()
336 .removeListener(fProblemChangedListener);
337 fProblemChangedListener = null;
339 if (fRegistry != null && fUseNewRegistry) {
347 * @see IBaseLabelProvider#isLabelProperty(Object, String)
349 public boolean isLabelProperty(Object element, String property) {
356 * @see IBaseLabelProvider#addListener(ILabelProviderListener)
358 public void addListener(ILabelProviderListener listener) {
359 if (fListeners == null) {
360 fListeners = new ListenerList();
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);
370 PHPeclipsePlugin.getDefault().getProblemMarkerManager()
371 .addListener(fProblemChangedListener);
378 * @see IBaseLabelProvider#removeListener(ILabelProviderListener)
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;
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);
407 * @see org.eclipse.jface.viewers.ILightweightLabelDecorator#decorate(java.lang.Object,
408 * org.eclipse.jface.viewers.IDecoration)
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);