Added
Link Here
|
1 |
package org.eclipse.jdt.internal.ui.javaeditor; |
2 |
|
3 |
import java.util.Iterator; |
4 |
|
5 |
import org.eclipse.swt.custom.StyledText; |
6 |
import org.eclipse.swt.graphics.Color; |
7 |
import org.eclipse.swt.graphics.GC; |
8 |
|
9 |
import org.eclipse.jface.text.BadLocationException; |
10 |
import org.eclipse.jface.text.IDocument; |
11 |
import org.eclipse.jface.text.IPainter; |
12 |
import org.eclipse.jface.text.IRegion; |
13 |
import org.eclipse.jface.text.Position; |
14 |
import org.eclipse.jface.text.source.Annotation; |
15 |
import org.eclipse.jface.text.source.AnnotationModel; |
16 |
import org.eclipse.jface.text.source.AnnotationPainter; |
17 |
import org.eclipse.jface.text.source.IAnnotationAccess; |
18 |
import org.eclipse.jface.text.source.IAnnotationModel; |
19 |
import org.eclipse.jface.text.source.IAnnotationModelExtension; |
20 |
import org.eclipse.jface.text.source.ISourceViewer; |
21 |
import org.eclipse.jface.text.source.ISourceViewerExtension2; |
22 |
|
23 |
import org.eclipse.ui.texteditor.IDocumentProvider; |
24 |
|
25 |
import org.eclipse.jdt.core.ElementChangedEvent; |
26 |
import org.eclipse.jdt.core.IElementChangedListener; |
27 |
import org.eclipse.jdt.core.IJavaElement; |
28 |
import org.eclipse.jdt.core.IJavaElementDelta; |
29 |
import org.eclipse.jdt.core.IParent; |
30 |
import org.eclipse.jdt.core.ISourceRange; |
31 |
import org.eclipse.jdt.core.ISourceReference; |
32 |
import org.eclipse.jdt.core.IType; |
33 |
import org.eclipse.jdt.core.ITypeRoot; |
34 |
import org.eclipse.jdt.core.JavaCore; |
35 |
import org.eclipse.jdt.core.JavaModelException; |
36 |
|
37 |
import org.eclipse.jdt.internal.ui.JavaPlugin; |
38 |
|
39 |
/** |
40 |
* Draws lines that visually demarcate the boundary for methods and types. |
41 |
* |
42 |
* @since 3.7 |
43 |
*/ |
44 |
class MethodBoundaryLinesProvider { |
45 |
|
46 |
/** |
47 |
* Updates the annotation model to reflect the incoming element changes. |
48 |
*/ |
49 |
private class ElementChangedListener implements IElementChangedListener { |
50 |
|
51 |
/* |
52 |
* @see org.eclipse.jdt.core.IElementChangedListener#elementChanged(org.eclipse.jdt.core.ElementChangedEvent) |
53 |
*/ |
54 |
public void elementChanged(ElementChangedEvent event) { |
55 |
IJavaElementDelta delta= findElement(fInput, event.getDelta()); |
56 |
if (delta != null && (delta.getFlags() & (IJavaElementDelta.F_CHILDREN | IJavaElementDelta.F_CONTENT)) != 0) { |
57 |
//System.out.println("Element Changed: " + delta); |
58 |
if ((delta.getFlags() & IJavaElementDelta.F_CHILDREN) != 0) |
59 |
updateAnnotationModel(delta.getAffectedChildren()); |
60 |
else |
61 |
updateAnnotationPositions(); |
62 |
} |
63 |
} |
64 |
|
65 |
/** |
66 |
* Find the element in the java element delta that matches the target element. |
67 |
* |
68 |
* @param target the target java element to be searched for in the delta |
69 |
* @param delta the java element delta that is to be searched for the target element |
70 |
* @return the delta that contains the java element matching the target element |
71 |
*/ |
72 |
private IJavaElementDelta findElement(IJavaElement target, IJavaElementDelta delta) { |
73 |
|
74 |
if (delta == null || target == null) |
75 |
return null; |
76 |
|
77 |
IJavaElement element= delta.getElement(); |
78 |
|
79 |
if (element.getElementType() > IJavaElement.CLASS_FILE) |
80 |
return null; |
81 |
|
82 |
if (target.equals(element)) |
83 |
return delta; |
84 |
|
85 |
IJavaElementDelta[] children= delta.getAffectedChildren(); |
86 |
|
87 |
for (int i= 0; i < children.length; i++) { |
88 |
IJavaElementDelta d= findElement(target, children[i]); |
89 |
if (d != null) |
90 |
return d; |
91 |
} |
92 |
|
93 |
return null; |
94 |
} |
95 |
} |
96 |
|
97 |
/** |
98 |
* The annotation model that holds the annotations for method boundary lines. |
99 |
*/ |
100 |
private class MethodBoundaryAnnotationModel extends AnnotationModel { |
101 |
private MethodBoundaryAnnotationModel() { |
102 |
} |
103 |
|
104 |
/** |
105 |
* Sets the position for the annotation associated with a given element to the new position. |
106 |
* If position is null then the annotation, if found, is removed from the model. |
107 |
* |
108 |
* @param element the element whose corresponding annotation position is to be modified |
109 |
* @param newPosition The new position for the concerned annotation |
110 |
*/ |
111 |
public void modifyAnnotationForElement(IJavaElement element, Position newPosition) { |
112 |
MethodBoundaryAnnotation methodBoundaryAnnotation= getAnnotationForElement(element); |
113 |
if (methodBoundaryAnnotation != null) |
114 |
modifyAnnotationPosition(methodBoundaryAnnotation, newPosition); |
115 |
} |
116 |
|
117 |
/** |
118 |
* Searches and returns the annotation associated with given element. |
119 |
* |
120 |
* @param element the element whose associated annotation we are looking for |
121 |
* @return returns the associated annotation if found, else returns null |
122 |
*/ |
123 |
public MethodBoundaryAnnotation getAnnotationForElement(IJavaElement element) { |
124 |
Iterator iterator= getAnnotationIterator(); |
125 |
while (iterator.hasNext()) { |
126 |
MethodBoundaryAnnotation methodBoundaryAnnotation= (MethodBoundaryAnnotation)iterator.next(); |
127 |
if (element.equals(methodBoundaryAnnotation.getElement())) { |
128 |
return methodBoundaryAnnotation; |
129 |
} |
130 |
} |
131 |
return null; |
132 |
} |
133 |
} |
134 |
|
135 |
/** |
136 |
* The drawing strategy for method boundary lines draws the lines that visual separate methods |
137 |
* and types. |
138 |
*/ |
139 |
private static class MethodBoundaryDrawingStrategy implements AnnotationPainter.IDrawingStrategy { |
140 |
|
141 |
/* |
142 |
* @see org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy#draw(org.eclipse.swt.graphics.GC, org.eclipse.swt.custom.StyledText, int, int, org.eclipse.swt.graphics.Color) |
143 |
*/ |
144 |
public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) { |
145 |
if (annotation instanceof MethodBoundaryAnnotation) { |
146 |
MethodBoundaryAnnotation methodBoundaryAnnotation= (MethodBoundaryAnnotation)annotation; |
147 |
if (gc != null) { |
148 |
int lineIndex= textWidget.getLineAtOffset(offset); |
149 |
int lineY= textWidget.getLinePixel(lineIndex); |
150 |
IJavaElement element= methodBoundaryAnnotation.getElement(); |
151 |
if (element.getElementType() == IJavaElement.TYPE) { |
152 |
//System.out.println(methodBoundaryAnnotation.getElement().getElementName() + ": Off-" + offset + " Len-" + length + " Width-" + 2); |
153 |
gc.setLineWidth(2); |
154 |
lineY++; |
155 |
} else if (element.getElementType() == IJavaElement.METHOD) { |
156 |
//System.out.println(methodBoundaryAnnotation.getElement().getElementName() + ": Off-" + offset + " Len-" + length + " Width-" + 0); |
157 |
gc.setLineWidth(0); |
158 |
} else { |
159 |
//System.out.println(methodBoundaryAnnotation.getElement().getElementName() + ": Off-" + offset + " Len-" + length + " Width- ?"); |
160 |
if (methodBoundaryAnnotation.getPreviousElementType() == IJavaElement.TYPE) { |
161 |
gc.setLineWidth(2); |
162 |
lineY++; |
163 |
} else { |
164 |
gc.setLineWidth(0); |
165 |
} |
166 |
} |
167 |
Color defaultColor= gc.getForeground(); |
168 |
gc.setForeground(color); |
169 |
gc.drawLine(2, lineY, textWidget.getClientArea().width, lineY); |
170 |
gc.setForeground(defaultColor); |
171 |
} else { |
172 |
//System.out.println("Clearing: " + methodBoundaryAnnotation.getElement().getElementName() + ": " + offset + " " + length); |
173 |
textWidget.redraw(2, textWidget.getLinePixel(textWidget.getLineAtOffset(offset)), textWidget.getClientArea().width, 2, true); |
174 |
} |
175 |
} |
176 |
} |
177 |
} |
178 |
|
179 |
/** |
180 |
* Annotation for method boundary lines. |
181 |
*/ |
182 |
private class MethodBoundaryAnnotation extends Annotation { |
183 |
|
184 |
/** |
185 |
* The type for method boundary annotation. |
186 |
*/ |
187 |
public static final String TYPE= "org.eclipse.separator"; //$NON-NLS-1$ |
188 |
/** |
189 |
* The java element associated with this annotation. |
190 |
*/ |
191 |
private IJavaElement fElementToBeAnnotated; |
192 |
|
193 |
/** |
194 |
* The type of the element thats is previous to the element associated with this annotation. |
195 |
*/ |
196 |
private int fPreviousElementType; |
197 |
|
198 |
/** |
199 |
* Creates a new method boundary annotation. |
200 |
* |
201 |
* @param element the java element that this annotation is associated with |
202 |
* @param previousElementType the previous element type |
203 |
*/ |
204 |
public MethodBoundaryAnnotation(IJavaElement element, int previousElementType) { |
205 |
super(TYPE, false, null); |
206 |
fElementToBeAnnotated= element; |
207 |
fPreviousElementType= previousElementType; |
208 |
} |
209 |
|
210 |
/** |
211 |
* Getter for the java element associated with this annotation. |
212 |
* |
213 |
* @return returns the java element associated with this annotation |
214 |
*/ |
215 |
public IJavaElement getElement() { |
216 |
return fElementToBeAnnotated; |
217 |
} |
218 |
|
219 |
/** |
220 |
* Getter for previous element type. |
221 |
* |
222 |
* @return returns the previous element type |
223 |
*/ |
224 |
public int getPreviousElementType() { |
225 |
return fPreviousElementType; |
226 |
} |
227 |
|
228 |
/** |
229 |
* Sets the previous element type. |
230 |
* |
231 |
* @param previousElementType the previous element type |
232 |
*/ |
233 |
public void setPreviousElementType(int previousElementType) { |
234 |
fPreviousElementType= previousElementType; |
235 |
} |
236 |
} |
237 |
|
238 |
/** |
239 |
* Paints the method boundary lines. |
240 |
*/ |
241 |
private class MethodBoundaryAnnotationsPainter extends AnnotationPainter { |
242 |
|
243 |
/** |
244 |
* Constructs a new <code>MethodSeparatorPainter</code>. |
245 |
* |
246 |
* @param sourceViewer the source viewer for the painter |
247 |
* @param access the annotation access |
248 |
*/ |
249 |
public MethodBoundaryAnnotationsPainter(ISourceViewer sourceViewer, IAnnotationAccess access) { |
250 |
super(sourceViewer, access); |
251 |
} |
252 |
|
253 |
/** |
254 |
* Retrieves the annotation model from the given source viewer. |
255 |
* |
256 |
* @param sourceViewer the source viewer |
257 |
* @return the source viewer's annotation model or <code>null</code> if none can be found |
258 |
*/ |
259 |
protected IAnnotationModel findAnnotationModel(ISourceViewer sourceViewer) { |
260 |
if (sourceViewer != null && sourceViewer instanceof ISourceViewerExtension2) |
261 |
return ((IAnnotationModelExtension)(((ISourceViewerExtension2)sourceViewer).getVisualAnnotationModel())).getAnnotationModel(METHOD_BOUNDARY); |
262 |
return null; |
263 |
} |
264 |
} |
265 |
|
266 |
/** |
267 |
* Key of the method boundary annotation model inside the visual annotation model. Also |
268 |
* internally used as key for the drawing strategy. |
269 |
*/ |
270 |
public final static Object METHOD_BOUNDARY= new Object(); |
271 |
/** |
272 |
* The source viewer associated with the editor. |
273 |
*/ |
274 |
private JavaSourceViewer fViewer; |
275 |
/** |
276 |
* The method boundary painter that paints the boundary lines. |
277 |
*/ |
278 |
private MethodBoundaryAnnotationsPainter fPainter; |
279 |
|
280 |
/** |
281 |
* The annotation access. |
282 |
*/ |
283 |
private IAnnotationAccess fAnnotationAccess; |
284 |
/** |
285 |
* The drawing strategy that is used for drawing the method boundary lines. |
286 |
*/ |
287 |
private static AnnotationPainter.IDrawingStrategy fDrawingStrategy; |
288 |
/** |
289 |
* The method boundary annotation model that holds the annotations and is connected to the |
290 |
* document. |
291 |
*/ |
292 |
private MethodBoundaryAnnotationModel fMethodBoundaryAnnotationModel; |
293 |
/** |
294 |
* The listener that listens for element changes from the java model. |
295 |
*/ |
296 |
private IElementChangedListener fElementListener; |
297 |
/** |
298 |
* The java input element associated with the java editor. |
299 |
*/ |
300 |
private IJavaElement fInput; |
301 |
/** |
302 |
* The java editor. |
303 |
*/ |
304 |
private JavaEditor fEditor; |
305 |
|
306 |
/** |
307 |
* The method boundary color. |
308 |
*/ |
309 |
private Color fMethodBoundaryColor; |
310 |
|
311 |
/** |
312 |
* Constructs the <code>MethodBoundaryLinesProvider</code>. |
313 |
* |
314 |
* @param editor the java editor |
315 |
* @param viewer the source viewer associated with the editor |
316 |
* @param annotationAccess the annotation access |
317 |
* @param methodBoundaryColor the color to be used for drawing the method boundary lines |
318 |
*/ |
319 |
public MethodBoundaryLinesProvider(JavaEditor editor, JavaSourceViewer viewer, IAnnotationAccess annotationAccess, Color methodBoundaryColor) { |
320 |
fEditor= editor; |
321 |
fViewer= viewer; |
322 |
fAnnotationAccess= annotationAccess; |
323 |
fMethodBoundaryColor= methodBoundaryColor; |
324 |
|
325 |
fInput= EditorUtility.getEditorInputJavaElement(fEditor, false); |
326 |
fMethodBoundaryAnnotationModel= new MethodBoundaryAnnotationModel(); |
327 |
try { |
328 |
initializeModel(((IParent)fInput).getChildren()); |
329 |
} catch (JavaModelException e) { |
330 |
JavaPlugin.log(e); |
331 |
} |
332 |
|
333 |
((IAnnotationModelExtension)(fViewer.getVisualAnnotationModel())).addAnnotationModel(METHOD_BOUNDARY, fMethodBoundaryAnnotationModel); |
334 |
if (fElementListener == null) { |
335 |
//System.out.println("New Listener Registered: " + fEditor.getContentDescription()); |
336 |
fElementListener= new ElementChangedListener(); |
337 |
JavaCore.addElementChangedListener(fElementListener); |
338 |
} |
339 |
} |
340 |
|
341 |
/** |
342 |
* Initializes the annotation model with annotations based on the array of java elements. |
343 |
* |
344 |
* @param elements the java elements for which the annotation model is to be initialized |
345 |
*/ |
346 |
private void initializeModel(IJavaElement[] elements) { |
347 |
for (int i= 0; i < elements.length; i++) { |
348 |
int elementType= elements[i].getElementType(); |
349 |
try { |
350 |
if (isElementOfTypeWeCanAnnotate(elements[i], true)) { |
351 |
if (isElementMethodOrType(elementType)) { |
352 |
fMethodBoundaryAnnotationModel.addAnnotation(new MethodBoundaryAnnotation(elements[i], 0), getAlignedFirstLineElementPosition(elements[i])); |
353 |
} else if (i != 0 && isElementMethodOrType(elements[i - 1].getElementType())) { |
354 |
fMethodBoundaryAnnotationModel.addAnnotation(new MethodBoundaryAnnotation(elements[i], elements[i - 1].getElementType()), getAlignedFirstLineElementPosition(elements[i])); |
355 |
} |
356 |
} |
357 |
if (elements[i] instanceof IParent) { |
358 |
IJavaElement[] children= ((IParent)elements[i]).getChildren(); |
359 |
if (children.length > 0) |
360 |
initializeModel(children); |
361 |
} |
362 |
} catch (JavaModelException e) { |
363 |
JavaPlugin.log(e); |
364 |
} |
365 |
} |
366 |
} |
367 |
|
368 |
/** |
369 |
* Get the aligned position of the first line of the java element. |
370 |
* |
371 |
* @param element the java element whose first line position is to be determined |
372 |
* @return returns the position of the first line of the java element |
373 |
*/ |
374 |
private Position getAlignedFirstLineElementPosition(IJavaElement element) { |
375 |
IDocument document= getDocument(); |
376 |
try { |
377 |
ISourceRange sourceRange= ((ISourceReference)element).getSourceRange(); |
378 |
IRegion elementFirstLineRegion= document.getLineInformationOfOffset(sourceRange.getOffset()); |
379 |
return new Position(elementFirstLineRegion.getOffset(), elementFirstLineRegion.getLength()); |
380 |
} catch (JavaModelException e) { |
381 |
JavaPlugin.log(e); |
382 |
} catch (BadLocationException e) { |
383 |
JavaPlugin.log(e); |
384 |
} |
385 |
return null; |
386 |
} |
387 |
|
388 |
/** |
389 |
* Returns <code>true</code> if the element type is one of the types that we can annotate and it |
390 |
* should not be of primary type if <code>checkForPrimaryType</code> is true. Types we can |
391 |
* annotate are {@link IJavaElement#TYPE}, METHOD, FIELD & INITIALIZER. |
392 |
* |
393 |
* @param element the element whose type is to be checked |
394 |
* @param checkForPrimaryType if <code>true</code> checks if the element is of primary type |
395 |
* @return <code>true</code> if element type matches any in our list |
396 |
*/ |
397 |
private boolean isElementOfTypeWeCanAnnotate(IJavaElement element, boolean checkForPrimaryType) { |
398 |
int elementType= element.getElementType(); |
399 |
if (element instanceof ISourceReference && (elementType == IJavaElement.TYPE |
400 |
|| elementType == IJavaElement.METHOD || elementType == IJavaElement.FIELD |
401 |
|| elementType == IJavaElement.INITIALIZER)) { |
402 |
if (elementType == IJavaElement.TYPE && checkForPrimaryType) |
403 |
return !isPrimaryType((IType)element); |
404 |
return true; |
405 |
} |
406 |
return false; |
407 |
} |
408 |
|
409 |
/** |
410 |
* Returns <code>true</code> if <code>type</code> is not a top-level type, <code>false</code> if |
411 |
* it is. |
412 |
* |
413 |
* @param type the type to test |
414 |
* @return <code>true</code> if <code>type</code> is an inner type |
415 |
*/ |
416 |
private boolean isInnerType(IType type) { |
417 |
return type.getDeclaringType() != null; |
418 |
} |
419 |
|
420 |
/** |
421 |
* Returns <code>true</code> if <code>element</code> is the primary type, <code>false</code> if |
422 |
* it is not. This is based on {@link ITypeRoot#findPrimaryType()}. |
423 |
* |
424 |
* @param element the <code>element</code> to be evaluated if it is the primary type |
425 |
* @return <code>true</code> if it is the primary type |
426 |
*/ |
427 |
private boolean isPrimaryType(IType element) { |
428 |
IType primaryType= ((ITypeRoot)fInput).findPrimaryType(); |
429 |
if (primaryType != null && primaryType.equals(element)) |
430 |
return true; |
431 |
return false; |
432 |
} |
433 |
|
434 |
/** |
435 |
* Update the annotations in the method boundary annotation model with the |
436 |
* {@link IJavaElementDelta} changes. Based on ({@link IJavaElementDelta#getKind()}, annotation |
437 |
* corresponding to the element in the delta is added, removed or changed. |
438 |
* |
439 |
* @param delta the java element deltas that contain the changes done to the Java model |
440 |
*/ |
441 |
private void updateAnnotationModel(IJavaElementDelta[] delta) { |
442 |
for (int i= 0; i < delta.length; i++) { |
443 |
IJavaElement element= delta[i].getElement(); |
444 |
if (isElementOfTypeWeCanAnnotate(element, false)) { |
445 |
int elementType= element.getElementType(); |
446 |
int deltaKind= delta[i].getKind(); |
447 |
if (deltaKind == IJavaElementDelta.REMOVED) { |
448 |
//System.out.println("Element 'Removed': " + element.getElementName()); |
449 |
fMethodBoundaryAnnotationModel.modifyAnnotationForElement(element, null); |
450 |
updateAnnotationModelForDanglingElements(element.getParent()); |
451 |
|
452 |
} else if (deltaKind == IJavaElementDelta.ADDED) { |
453 |
int previousElementType= getPreviousElementType(element); |
454 |
if (isElementMethodOrType(elementType) || previousElementType != 0) { |
455 |
addAnnotationForNewElementToModel(element, previousElementType); |
456 |
updateAnnotationModelForDanglingElements(element.getParent()); |
457 |
} |
458 |
} else if (deltaKind == IJavaElementDelta.CHANGED) { |
459 |
//System.out.println("Element 'Changed': " + element.getElementName()); |
460 |
fMethodBoundaryAnnotationModel.modifyAnnotationForElement(element, getAlignedFirstLineElementPosition(element)); |
461 |
} |
462 |
} |
463 |
if (element instanceof IParent) { |
464 |
updateAnnotationModel(delta[i].getAffectedChildren()); |
465 |
} |
466 |
} |
467 |
} |
468 |
|
469 |
private int getPreviousElementType(IJavaElement element) { |
470 |
try { |
471 |
IJavaElement parentElement= element.getParent(); |
472 |
if (parentElement != null && parentElement.getElementType() == IJavaElement.TYPE && ((IType)parentElement).hasChildren()) { |
473 |
IJavaElement[] childrenElements= ((IType)parentElement).getChildren(); |
474 |
for (int i= 0; i < childrenElements.length; i++) { |
475 |
if (childrenElements[i].equals(element)) { |
476 |
if (i == 0) |
477 |
return 0; |
478 |
int childElementType= childrenElements[i - 1].getElementType(); |
479 |
if (childElementType == IJavaElement.METHOD) |
480 |
return IJavaElement.METHOD; |
481 |
else if (childElementType == IJavaElement.TYPE) |
482 |
return IJavaElement.TYPE; |
483 |
return 0; |
484 |
} |
485 |
} |
486 |
} |
487 |
} catch (JavaModelException e) { |
488 |
JavaPlugin.log(e); |
489 |
} |
490 |
return 0; |
491 |
} |
492 |
|
493 |
/** |
494 |
* Return <code>true</code> if the given elementType is of type {@link IJavaElement#METHOD} or |
495 |
* {@link IJavaElement#TYPE}. |
496 |
* |
497 |
* @param elementType the element type to check against |
498 |
* @return <code>true</code> if the given elementType is of type {@link IJavaElement#METHOD} or |
499 |
* {@link IJavaElement#TYPE} |
500 |
*/ |
501 |
private boolean isElementMethodOrType(int elementType) { |
502 |
if (elementType == IJavaElement.METHOD || elementType == IJavaElement.TYPE) |
503 |
return true; |
504 |
return false; |
505 |
} |
506 |
|
507 |
/** |
508 |
* For all the children of parentElement, it checks and updates if any does not have a required |
509 |
* Method boundary. |
510 |
* |
511 |
* @param parentElement the parent element whose children are checked for missing boundary lines |
512 |
*/ |
513 |
private void updateAnnotationModelForDanglingElements(IJavaElement parentElement) { |
514 |
try { |
515 |
if (parentElement != null && parentElement.getElementType() == IJavaElement.TYPE && ((IType)parentElement).hasChildren()) { |
516 |
IJavaElement[] childrenElements= ((IType)parentElement).getChildren(); |
517 |
for (int i= 0; i < childrenElements.length; i++) { |
518 |
if (!isElementMethodOrType(childrenElements[i].getElementType()) && isElementOfTypeWeCanAnnotate(childrenElements[i], true)) { |
519 |
if (i == 0) { |
520 |
fMethodBoundaryAnnotationModel.modifyAnnotationForElement(childrenElements[i], null); |
521 |
} else { |
522 |
int previousElementType= childrenElements[i - 1].getElementType(); |
523 |
if (isElementMethodOrType(previousElementType)) { |
524 |
MethodBoundaryAnnotation methodBoundaryAnnotation= fMethodBoundaryAnnotationModel.getAnnotationForElement(childrenElements[i]); |
525 |
if (methodBoundaryAnnotation == null) { |
526 |
fMethodBoundaryAnnotationModel.addAnnotation(new MethodBoundaryAnnotation(childrenElements[i], previousElementType), |
527 |
getAlignedFirstLineElementPosition(childrenElements[i])); |
528 |
} else { |
529 |
methodBoundaryAnnotation.setPreviousElementType(previousElementType); |
530 |
Position position= fMethodBoundaryAnnotationModel.getPosition(methodBoundaryAnnotation); |
531 |
fMethodBoundaryAnnotationModel.modifyAnnotationPosition(methodBoundaryAnnotation, null); |
532 |
fMethodBoundaryAnnotationModel.addAnnotation(methodBoundaryAnnotation, position); |
533 |
} |
534 |
} else { |
535 |
fMethodBoundaryAnnotationModel.modifyAnnotationForElement(childrenElements[i], null); |
536 |
} |
537 |
} |
538 |
} |
539 |
} |
540 |
} |
541 |
} catch (JavaModelException e) { |
542 |
JavaPlugin.log(e); |
543 |
} |
544 |
} |
545 |
|
546 |
/** |
547 |
* Returns <code>true</code> if the given element is immediately preceded by an element of type |
548 |
* IJavaElement.METHOD or TYPE in the source code. |
549 |
* |
550 |
* @param element the element |
551 |
* @return <code>true</code> if the given element is preceded by an IMethod or IType element |
552 |
*/ |
553 |
private boolean isElementAfterTypeOrMethod(IJavaElement element) { |
554 |
try { |
555 |
IJavaElement parentElement= element.getParent(); |
556 |
if (parentElement != null && parentElement.getElementType() == IJavaElement.TYPE && ((IType)parentElement).hasChildren()) { |
557 |
IJavaElement[] childrenElements= ((IType)parentElement).getChildren(); |
558 |
for (int i= 0; i < childrenElements.length; i++) { |
559 |
if (childrenElements[i].equals(element)) { |
560 |
if (i == 0) |
561 |
return false; |
562 |
int childElementType= childrenElements[i - 1].getElementType(); |
563 |
if (childElementType == IJavaElement.METHOD || (childElementType == IJavaElement.TYPE && isInnerType((IType)childrenElements[i - 1]))) { |
564 |
return true; |
565 |
} |
566 |
} |
567 |
} |
568 |
} |
569 |
} catch (JavaModelException e) { |
570 |
JavaPlugin.log(e); |
571 |
} |
572 |
return false; |
573 |
} |
574 |
|
575 |
/** |
576 |
* For a new element added to the Java, create and add an annotation to the method boundary |
577 |
* annotation model. |
578 |
* |
579 |
* @param element the new element for which annotation is to be added to the annotation model |
580 |
* @param previousElementType the type of the previous element |
581 |
*/ |
582 |
private void addAnnotationForNewElementToModel(IJavaElement element, int previousElementType) { |
583 |
//System.out.println("Element 'Added': " + element.getElementName()); |
584 |
if (isElementOfTypeWeCanAnnotate(element, true)) |
585 |
fMethodBoundaryAnnotationModel.addAnnotation(new MethodBoundaryAnnotation(element, previousElementType), getAlignedFirstLineElementPosition(element)); |
586 |
if (element instanceof IParent) { |
587 |
try { |
588 |
IJavaElement[] children= ((IParent)element).getChildren(); |
589 |
for (int i= 0; i < children.length; i++) { |
590 |
int elementType= children[i].getElementType(); |
591 |
if (isElementMethodOrType(elementType) || isElementAfterTypeOrMethod(children[i])) { |
592 |
if (i != 0) |
593 |
previousElementType= children[i - 1].getElementType(); |
594 |
else |
595 |
previousElementType= 0; |
596 |
addAnnotationForNewElementToModel(children[i], previousElementType); |
597 |
} |
598 |
} |
599 |
} catch (JavaModelException e) { |
600 |
JavaPlugin.log(e); |
601 |
} |
602 |
} |
603 |
} |
604 |
|
605 |
/** |
606 |
* Update the Positions in the annotation model for all the elements whose current source range |
607 |
* in the document is different from the Position stored with the corresponding annotation. |
608 |
*/ |
609 |
private void updateAnnotationPositions() { |
610 |
Iterator iterator= fMethodBoundaryAnnotationModel.getAnnotationIterator(); |
611 |
while (iterator.hasNext()) { |
612 |
MethodBoundaryAnnotation methodBoundaryAnnotation= (MethodBoundaryAnnotation)iterator.next(); |
613 |
Position existingPosition= fMethodBoundaryAnnotationModel.getPosition(methodBoundaryAnnotation); |
614 |
IJavaElement element= methodBoundaryAnnotation.getElement(); |
615 |
if (element != null && ((ISourceReference)element).exists()) { |
616 |
Position newPosition= getAlignedFirstLineElementPosition(element); |
617 |
if (newPosition.getOffset() != existingPosition.getOffset() || newPosition.getLength() != existingPosition.getLength()) { |
618 |
fMethodBoundaryAnnotationModel.modifyAnnotationPosition(methodBoundaryAnnotation, null); |
619 |
fMethodBoundaryAnnotationModel.modifyAnnotationPosition(methodBoundaryAnnotation, newPosition); |
620 |
} |
621 |
} |
622 |
} |
623 |
} |
624 |
|
625 |
/** |
626 |
* Gets the document associated with the Java editor. |
627 |
* |
628 |
* @return returns the document |
629 |
*/ |
630 |
private IDocument getDocument() { |
631 |
JavaEditor editor= fEditor; |
632 |
if (editor == null) |
633 |
return null; |
634 |
|
635 |
IDocumentProvider provider= editor.getDocumentProvider(); |
636 |
if (provider == null) |
637 |
return null; |
638 |
|
639 |
return provider.getDocument(editor.getEditorInput()); |
640 |
} |
641 |
|
642 |
/** |
643 |
* Returns the drawing strategy to be used by the method boundary annotation painter. |
644 |
* |
645 |
* @return the drawing strategy to be used by the method boundary annotation painter |
646 |
*/ |
647 |
private AnnotationPainter.IDrawingStrategy getDrawingStrategy() { |
648 |
if (fDrawingStrategy == null) |
649 |
fDrawingStrategy= new MethodBoundaryDrawingStrategy(); |
650 |
return fDrawingStrategy; |
651 |
} |
652 |
|
653 |
/** |
654 |
* Disposes the method boundary lines provider and associated painter. |
655 |
*/ |
656 |
public void dispose() { |
657 |
if (fPainter != null) { |
658 |
fViewer.removePainter(fPainter); |
659 |
fPainter.deactivate(true); |
660 |
fPainter.dispose(); |
661 |
fPainter= null; |
662 |
} |
663 |
IAnnotationModelExtension visualModel= ((IAnnotationModelExtension)(fViewer.getVisualAnnotationModel())); |
664 |
if (visualModel != null) |
665 |
visualModel.removeAnnotationModel(METHOD_BOUNDARY); |
666 |
fMethodBoundaryAnnotationModel= null; |
667 |
JavaCore.removeElementChangedListener(fElementListener); |
668 |
fViewer= null; |
669 |
fEditor= null; |
670 |
fMethodBoundaryColor= null; |
671 |
} |
672 |
|
673 |
/** |
674 |
* Sets the color for the method boundary lines. |
675 |
* |
676 |
* @param methodBoundaryColor the color to be used for drawing the method boundary lines |
677 |
*/ |
678 |
public void setColor(Color methodBoundaryColor) { |
679 |
fMethodBoundaryColor= methodBoundaryColor; |
680 |
fPainter.setAnnotationTypeColor(MethodBoundaryAnnotation.TYPE, fMethodBoundaryColor); |
681 |
fPainter.paint(IPainter.CONFIGURATION); |
682 |
} |
683 |
|
684 |
/** |
685 |
* Deactivates the boundary line drawing. |
686 |
*/ |
687 |
public void deactivate() { |
688 |
if (fPainter != null) { |
689 |
fViewer.removePainter(fPainter); |
690 |
//fPainter.deactivate(true); |
691 |
fPainter.dispose(); |
692 |
fPainter= null; |
693 |
} |
694 |
} |
695 |
|
696 |
/** |
697 |
* Activates the boundary line drawing. |
698 |
*/ |
699 |
public void activate() { |
700 |
if (fPainter == null) { |
701 |
fPainter= new MethodBoundaryAnnotationsPainter(fViewer, fAnnotationAccess); |
702 |
fPainter.addDrawingStrategy(METHOD_BOUNDARY, getDrawingStrategy()); |
703 |
fPainter.addAnnotationType(MethodBoundaryAnnotation.TYPE, METHOD_BOUNDARY); |
704 |
fPainter.setAnnotationTypeColor(MethodBoundaryAnnotation.TYPE, fMethodBoundaryColor); |
705 |
fViewer.addPainter(fPainter); |
706 |
} |
707 |
} |
708 |
} |