001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.ui;
011:
012: import java.util.Iterator;
013:
014: import org.eclipse.core.runtime.CoreException;
015: import org.eclipse.core.runtime.ListenerList;
016:
017: import org.eclipse.core.resources.IFile;
018: import org.eclipse.core.resources.IMarker;
019: import org.eclipse.core.resources.IResource;
020: import org.eclipse.core.resources.IResourceStatus;
021:
022: import org.eclipse.swt.graphics.Image;
023: import org.eclipse.swt.graphics.Point;
024: import org.eclipse.swt.graphics.Rectangle;
025:
026: import org.eclipse.jface.resource.ImageDescriptor;
027: import org.eclipse.jface.viewers.IBaseLabelProvider;
028: import org.eclipse.jface.viewers.IDecoration;
029: import org.eclipse.jface.viewers.ILabelDecorator;
030: import org.eclipse.jface.viewers.ILabelProviderListener;
031: import org.eclipse.jface.viewers.ILightweightLabelDecorator;
032: import org.eclipse.jface.viewers.LabelProviderChangedEvent;
033:
034: import org.eclipse.jface.text.Position;
035: import org.eclipse.jface.text.source.Annotation;
036: import org.eclipse.jface.text.source.IAnnotationModel;
037:
038: import org.eclipse.ui.part.FileEditorInput;
039: import org.eclipse.ui.texteditor.MarkerAnnotation;
040:
041: import org.eclipse.jdt.core.ICompilationUnit;
042: import org.eclipse.jdt.core.IJavaElement;
043: import org.eclipse.jdt.core.ISourceRange;
044: import org.eclipse.jdt.core.ISourceReference;
045: import org.eclipse.jdt.core.JavaModelException;
046:
047: import org.eclipse.jdt.internal.ui.JavaPlugin;
048: import org.eclipse.jdt.internal.ui.JavaPluginImages;
049: import org.eclipse.jdt.internal.ui.viewsupport.IProblemChangedListener;
050: import org.eclipse.jdt.internal.ui.viewsupport.ImageDescriptorRegistry;
051: import org.eclipse.jdt.internal.ui.viewsupport.ImageImageDescriptor;
052:
053: /**
054: * LabelDecorator that decorates an element's image with error and warning overlays that
055: * represent the severity of markers attached to the element's underlying resource. To see
056: * a problem decoration for a marker, the marker needs to be a subtype of <code>IMarker.PROBLEM</code>.
057: * <p>
058: * <b>Important</b>: Although this decorator implements ILightweightLabelDecorator, do not contribute this
059: * class as a decorator to the <code>org.eclipse.ui.decorators</code> extension. Only use this class in your
060: * own views and label providers.
061: *
062: * @since 2.0
063: */
064: public class ProblemsLabelDecorator implements ILabelDecorator,
065: ILightweightLabelDecorator {
066:
067: /**
068: * This is a special <code>LabelProviderChangedEvent</code> carrying additional
069: * information whether the event origins from a maker change.
070: * <p>
071: * <code>ProblemsLabelChangedEvent</code>s are only generated by <code>
072: * ProblemsLabelDecorator</code>s.
073: * </p>
074: */
075: public static class ProblemsLabelChangedEvent extends
076: LabelProviderChangedEvent {
077:
078: private static final long serialVersionUID = 1L;
079:
080: private boolean fMarkerChange;
081:
082: /**
083: * Note: This constructor is for internal use only. Clients should not call this constructor.
084: *
085: * @param eventSource the base label provider
086: * @param changedResource the changed resources
087: * @param isMarkerChange <code>true</code> if the change is a marker change; otherwise
088: * <code>false</code>
089: */
090: public ProblemsLabelChangedEvent(
091: IBaseLabelProvider eventSource,
092: IResource[] changedResource, boolean isMarkerChange) {
093: super (eventSource, changedResource);
094: fMarkerChange = isMarkerChange;
095: }
096:
097: /**
098: * Returns whether this event origins from marker changes. If <code>false</code> an annotation
099: * model change is the origin. In this case viewers not displaying working copies can ignore these
100: * events.
101: *
102: * @return if this event origins from a marker change.
103: */
104: public boolean isMarkerChange() {
105: return fMarkerChange;
106: }
107:
108: }
109:
110: private static final int ERRORTICK_WARNING = JavaElementImageDescriptor.WARNING;
111: private static final int ERRORTICK_ERROR = JavaElementImageDescriptor.ERROR;
112:
113: private ImageDescriptorRegistry fRegistry;
114: private boolean fUseNewRegistry = false;
115: private IProblemChangedListener fProblemChangedListener;
116:
117: private ListenerList fListeners;
118: private ISourceRange fCachedRange;
119:
120: /**
121: * Creates a new <code>ProblemsLabelDecorator</code>.
122: */
123: public ProblemsLabelDecorator() {
124: this (null);
125: fUseNewRegistry = true;
126: }
127:
128: /**
129: * Note: This constructor is for internal use only. Clients should not call this constructor.
130: *
131: * @param registry The registry to use or <code>null</code> to use the Java plugin's
132: * image registry
133: */
134: public ProblemsLabelDecorator(ImageDescriptorRegistry registry) {
135: fRegistry = registry;
136: fProblemChangedListener = null;
137: }
138:
139: private ImageDescriptorRegistry getRegistry() {
140: if (fRegistry == null) {
141: fRegistry = fUseNewRegistry ? new ImageDescriptorRegistry()
142: : JavaPlugin.getImageDescriptorRegistry();
143: }
144: return fRegistry;
145: }
146:
147: /* (non-Javadoc)
148: * @see ILabelDecorator#decorateText(String, Object)
149: */
150: public String decorateText(String text, Object element) {
151: return text;
152: }
153:
154: /* (non-Javadoc)
155: * @see ILabelDecorator#decorateImage(Image, Object)
156: */
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,
164: adornmentFlags, new Point(bounds.width,
165: bounds.height)));
166: }
167: return image;
168: }
169:
170: /**
171: * Note: This method is for internal use only. Clients should not call this method.
172: *
173: * @param obj the element to compute the flags for
174: *
175: * @return the adornment flags
176: */
177: protected int computeAdornmentFlags(Object obj) {
178: try {
179: if (obj instanceof IJavaElement) {
180: IJavaElement element = (IJavaElement) obj;
181: int type = element.getElementType();
182: switch (type) {
183: case IJavaElement.JAVA_MODEL:
184: case IJavaElement.JAVA_PROJECT:
185: case IJavaElement.PACKAGE_FRAGMENT_ROOT:
186: return getErrorTicksFromMarkers(element
187: .getResource(), IResource.DEPTH_INFINITE,
188: null);
189: case IJavaElement.PACKAGE_FRAGMENT:
190: case IJavaElement.COMPILATION_UNIT:
191: case IJavaElement.CLASS_FILE:
192: return getErrorTicksFromMarkers(element
193: .getResource(), IResource.DEPTH_ONE, null);
194: case IJavaElement.PACKAGE_DECLARATION:
195: case IJavaElement.IMPORT_DECLARATION:
196: case IJavaElement.IMPORT_CONTAINER:
197: case IJavaElement.TYPE:
198: case IJavaElement.INITIALIZER:
199: case IJavaElement.METHOD:
200: case IJavaElement.FIELD:
201: case IJavaElement.LOCAL_VARIABLE:
202: ICompilationUnit cu = (ICompilationUnit) element
203: .getAncestor(IJavaElement.COMPILATION_UNIT);
204: if (cu != null) {
205: ISourceReference ref = (type == IJavaElement.COMPILATION_UNIT) ? null
206: : (ISourceReference) element;
207: // The assumption is that only source elements in compilation unit can have markers
208: IAnnotationModel model = isInJavaAnnotationModel(cu);
209: int result = 0;
210: if (model != null) {
211: // open in Java editor: look at annotation model
212: result = getErrorTicksFromAnnotationModel(
213: model, ref);
214: } else {
215: result = getErrorTicksFromMarkers(cu
216: .getResource(),
217: IResource.DEPTH_ONE, ref);
218: }
219: fCachedRange = null;
220: return result;
221: }
222: break;
223: default:
224: }
225: } else if (obj instanceof IResource) {
226: return getErrorTicksFromMarkers((IResource) obj,
227: IResource.DEPTH_INFINITE, null);
228: }
229: } catch (CoreException e) {
230: if (e instanceof JavaModelException) {
231: if (((JavaModelException) e).isDoesNotExist()) {
232: return 0;
233: }
234: }
235: if (e.getStatus().getCode() == IResourceStatus.MARKER_NOT_FOUND) {
236: return 0;
237: }
238:
239: JavaPlugin.log(e);
240: }
241: return 0;
242: }
243:
244: private int getErrorTicksFromMarkers(IResource res, int depth,
245: ISourceReference sourceElement) throws CoreException {
246: if (res == null || !res.isAccessible()) {
247: return 0;
248: }
249: int severity = 0;
250: if (sourceElement == null) {
251: severity = res.findMaxProblemSeverity(IMarker.PROBLEM,
252: true, depth);
253: } else {
254: IMarker[] markers = res.findMarkers(IMarker.PROBLEM, true,
255: depth);
256: if (markers != null && markers.length > 0) {
257: for (int i = 0; i < markers.length
258: && (severity != IMarker.SEVERITY_ERROR); i++) {
259: IMarker curr = markers[i];
260: if (isMarkerInRange(curr, sourceElement)) {
261: int val = curr.getAttribute(IMarker.SEVERITY,
262: -1);
263: if (val == IMarker.SEVERITY_WARNING
264: || val == IMarker.SEVERITY_ERROR) {
265: severity = val;
266: }
267: }
268: }
269: }
270: }
271: if (severity == IMarker.SEVERITY_ERROR) {
272: return ERRORTICK_ERROR;
273: } else if (severity == IMarker.SEVERITY_WARNING) {
274: return ERRORTICK_WARNING;
275: }
276: return 0;
277: }
278:
279: private boolean isMarkerInRange(IMarker marker,
280: ISourceReference sourceElement) throws CoreException {
281: if (marker.isSubtypeOf(IMarker.TEXT)) {
282: int pos = marker.getAttribute(IMarker.CHAR_START, -1);
283: return isInside(pos, sourceElement);
284: }
285: return false;
286: }
287:
288: private IAnnotationModel isInJavaAnnotationModel(
289: ICompilationUnit original) {
290: if (original.isWorkingCopy()) {
291: FileEditorInput editorInput = new FileEditorInput(
292: (IFile) original.getResource());
293: return JavaPlugin.getDefault()
294: .getCompilationUnitDocumentProvider()
295: .getAnnotationModel(editorInput);
296: }
297: return null;
298: }
299:
300: private int getErrorTicksFromAnnotationModel(
301: IAnnotationModel model, ISourceReference sourceElement)
302: throws CoreException {
303: int info = 0;
304: Iterator iter = model.getAnnotationIterator();
305: while ((info != ERRORTICK_ERROR) && iter.hasNext()) {
306: Annotation annot = (Annotation) iter.next();
307: IMarker marker = isAnnotationInRange(model, annot,
308: sourceElement);
309: if (marker != null) {
310: int priority = marker
311: .getAttribute(IMarker.SEVERITY, -1);
312: if (priority == IMarker.SEVERITY_WARNING) {
313: info = ERRORTICK_WARNING;
314: } else if (priority == IMarker.SEVERITY_ERROR) {
315: info = ERRORTICK_ERROR;
316: }
317: }
318: }
319: return info;
320: }
321:
322: private IMarker isAnnotationInRange(IAnnotationModel model,
323: Annotation annot, ISourceReference sourceElement)
324: throws CoreException {
325: if (annot instanceof MarkerAnnotation) {
326: if (sourceElement == null
327: || isInside(model.getPosition(annot), sourceElement)) {
328: IMarker marker = ((MarkerAnnotation) annot).getMarker();
329: if (marker.exists()
330: && marker.isSubtypeOf(IMarker.PROBLEM)) {
331: return marker;
332: }
333: }
334: }
335: return null;
336: }
337:
338: private boolean isInside(Position pos,
339: ISourceReference sourceElement) throws CoreException {
340: return pos != null && isInside(pos.getOffset(), sourceElement);
341: }
342:
343: /**
344: * Tests if a position is inside the source range of an element.
345: * @param pos Position to be tested.
346: * @param sourceElement Source element (must be a IJavaElement)
347: * @return boolean Return <code>true</code> if position is located inside the source element.
348: * @throws CoreException Exception thrown if element range could not be accessed.
349: *
350: * @since 2.1
351: */
352: protected boolean isInside(int pos, ISourceReference sourceElement)
353: throws CoreException {
354: if (fCachedRange == null) {
355: fCachedRange = sourceElement.getSourceRange();
356: }
357: ISourceRange range = fCachedRange;
358: if (range != null) {
359: int rangeOffset = range.getOffset();
360: return (rangeOffset <= pos && rangeOffset
361: + range.getLength() > pos);
362: }
363: return false;
364: }
365:
366: /* (non-Javadoc)
367: * @see IBaseLabelProvider#dispose()
368: */
369: public void dispose() {
370: if (fProblemChangedListener != null) {
371: JavaPlugin.getDefault().getProblemMarkerManager()
372: .removeListener(fProblemChangedListener);
373: fProblemChangedListener = null;
374: }
375: if (fRegistry != null && fUseNewRegistry) {
376: fRegistry.dispose();
377: }
378: }
379:
380: /* (non-Javadoc)
381: * @see IBaseLabelProvider#isLabelProperty(Object, String)
382: */
383: public boolean isLabelProperty(Object element, String property) {
384: return true;
385: }
386:
387: /* (non-Javadoc)
388: * @see IBaseLabelProvider#addListener(ILabelProviderListener)
389: */
390: public void addListener(ILabelProviderListener listener) {
391: if (fListeners == null) {
392: fListeners = new ListenerList();
393: }
394: fListeners.add(listener);
395: if (fProblemChangedListener == null) {
396: fProblemChangedListener = new IProblemChangedListener() {
397: public void problemsChanged(
398: IResource[] changedResources,
399: boolean isMarkerChange) {
400: fireProblemsChanged(changedResources,
401: isMarkerChange);
402: }
403: };
404: JavaPlugin.getDefault().getProblemMarkerManager()
405: .addListener(fProblemChangedListener);
406: }
407: }
408:
409: /* (non-Javadoc)
410: * @see IBaseLabelProvider#removeListener(ILabelProviderListener)
411: */
412: public void removeListener(ILabelProviderListener listener) {
413: if (fListeners != null) {
414: fListeners.remove(listener);
415: if (fListeners.isEmpty() && fProblemChangedListener != null) {
416: JavaPlugin.getDefault().getProblemMarkerManager()
417: .removeListener(fProblemChangedListener);
418: fProblemChangedListener = null;
419: }
420: }
421: }
422:
423: private void fireProblemsChanged(IResource[] changedResources,
424: boolean isMarkerChange) {
425: if (fListeners != null && !fListeners.isEmpty()) {
426: LabelProviderChangedEvent event = new ProblemsLabelChangedEvent(
427: this , changedResources, isMarkerChange);
428: Object[] listeners = fListeners.getListeners();
429: for (int i = 0; i < listeners.length; i++) {
430: ((ILabelProviderListener) listeners[i])
431: .labelProviderChanged(event);
432: }
433: }
434: }
435:
436: /* (non-Javadoc)
437: * @see org.eclipse.jface.viewers.ILightweightLabelDecorator#decorate(java.lang.Object, org.eclipse.jface.viewers.IDecoration)
438: */
439: public void decorate(Object element, IDecoration decoration) {
440: int adornmentFlags = computeAdornmentFlags(element);
441: if (adornmentFlags == ERRORTICK_ERROR) {
442: decoration.addOverlay(JavaPluginImages.DESC_OVR_ERROR);
443: } else if (adornmentFlags == ERRORTICK_WARNING) {
444: decoration.addOverlay(JavaPluginImages.DESC_OVR_WARNING);
445: }
446: }
447:
448: }
|