]> git.uio.no Git - ifi-stolz-refaktor.git/blame - case-study/jdt-after/ui/org/eclipse/jdt/ui/text/folding/DefaultJavaFoldingStructureProvider.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-after / ui / org / eclipse / jdt / ui / text / folding / DefaultJavaFoldingStructureProvider.java
CommitLineData
1b2798f6
EK
1/*******************************************************************************
2 * Copyright (c) 2006, 2011 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11package org.eclipse.jdt.ui.text.folding;
12
13import java.util.ArrayList;
14import java.util.Arrays;
15import java.util.Collection;
16import java.util.Collections;
17import java.util.Comparator;
18import java.util.HashMap;
19import java.util.HashSet;
20import java.util.Iterator;
21import java.util.LinkedHashMap;
22import java.util.List;
23import java.util.Map;
24import java.util.Set;
25
26import org.eclipse.core.runtime.Assert;
27
28import org.eclipse.jface.preference.IPreferenceStore;
29
30import org.eclipse.jface.text.BadLocationException;
31import org.eclipse.jface.text.IDocument;
32import org.eclipse.jface.text.IRegion;
33import org.eclipse.jface.text.Position;
34import org.eclipse.jface.text.Region;
35import org.eclipse.jface.text.source.Annotation;
36import org.eclipse.jface.text.source.projection.IProjectionListener;
37import org.eclipse.jface.text.source.projection.IProjectionPosition;
38import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
39import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
40import org.eclipse.jface.text.source.projection.ProjectionViewer;
41
42import org.eclipse.ui.texteditor.ITextEditor;
43
44import org.eclipse.jdt.core.ElementChangedEvent;
45import org.eclipse.jdt.core.IClassFile;
46import org.eclipse.jdt.core.ICompilationUnit;
47import org.eclipse.jdt.core.IElementChangedListener;
48import org.eclipse.jdt.core.IJavaElement;
49import org.eclipse.jdt.core.IJavaElementDelta;
50import org.eclipse.jdt.core.IMember;
51import org.eclipse.jdt.core.IParent;
52import org.eclipse.jdt.core.ISourceRange;
53import org.eclipse.jdt.core.ISourceReference;
54import org.eclipse.jdt.core.IType;
55import org.eclipse.jdt.core.JavaCore;
56import org.eclipse.jdt.core.JavaModelException;
57import org.eclipse.jdt.core.SourceRange;
58import org.eclipse.jdt.core.ToolFactory;
59import org.eclipse.jdt.core.compiler.IScanner;
60import org.eclipse.jdt.core.compiler.ITerminalSymbols;
61import org.eclipse.jdt.core.compiler.InvalidInputException;
62import org.eclipse.jdt.core.dom.CompilationUnit;
63
64import org.eclipse.jdt.ui.PreferenceConstants;
65
66import org.eclipse.jdt.internal.ui.JavaPlugin;
67import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
68import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
69import org.eclipse.jdt.internal.ui.text.DocumentCharacterIterator;
70
71/**
72 * Updates the projection model of a class file or compilation unit.
73 * <p>
74 * Clients may instantiate or subclass. Subclasses must make sure to always call the superclass'
75 * code when overriding methods that are marked with "subclasses may extend".
76 * </p>
77 *
78 * @since 3.0 (internal)
79 * @since 3.2 (API)
80 */
81public class DefaultJavaFoldingStructureProvider implements IJavaFoldingStructureProvider, IJavaFoldingStructureProviderExtension {
82 /**
83 * A context that contains the information needed to compute the folding structure of an
84 * {@link ICompilationUnit} or an {@link IClassFile}. Computed folding regions are collected
85 * via
86 * {@linkplain #addProjectionRange(DefaultJavaFoldingStructureProvider.JavaProjectionAnnotation, Position) addProjectionRange}.
87 */
88 protected final class FoldingStructureComputationContext {
89 private final ProjectionAnnotationModel fModel;
90 private final IDocument fDocument;
91
92 private final boolean fAllowCollapsing;
93
94 private IType fFirstType;
95 private boolean fHasHeaderComment;
96 private LinkedHashMap<JavaProjectionAnnotation, Position> fMap= new LinkedHashMap<JavaProjectionAnnotation, Position>();
97 private IScanner fScanner;
98
99 private FoldingStructureComputationContext(IDocument document, ProjectionAnnotationModel model, boolean allowCollapsing, IScanner scanner) {
100 Assert.isNotNull(document);
101 Assert.isNotNull(model);
102 fDocument= document;
103 fModel= model;
104 fAllowCollapsing= allowCollapsing;
105 fScanner= scanner;
106 }
107
108 void setFirstType(IType type) {
109 if (hasFirstType())
110 throw new IllegalStateException();
111 fFirstType= type;
112 }
113
114 boolean hasFirstType() {
115 return fFirstType != null;
116 }
117
118 IType getFirstType() {
119 return fFirstType;
120 }
121
122 boolean hasHeaderComment() {
123 return fHasHeaderComment;
124 }
125
126 void setHasHeaderComment() {
127 fHasHeaderComment= true;
128 }
129
130 /**
131 * Returns <code>true</code> if newly created folding regions may be collapsed,
132 * <code>false</code> if not. This is usually <code>false</code> when updating the
133 * folding structure while typing; it may be <code>true</code> when computing or restoring
134 * the initial folding structure.
135 *
136 * @return <code>true</code> if newly created folding regions may be collapsed,
137 * <code>false</code> if not
138 */
139 public boolean allowCollapsing() {
140 return fAllowCollapsing;
141 }
142
143 /**
144 * Returns the document which contains the code being folded.
145 *
146 * @return the document which contains the code being folded
147 */
148 private IDocument getDocument() {
149 return fDocument;
150 }
151
152 ProjectionAnnotationModel getModel() {
153 return fModel;
154 }
155
156 IScanner getScanner() {
157 if (fScanner == null)
158 fScanner= ToolFactory.createScanner(true, false, false, false);
159 return fScanner;
160 }
161
162 /**
163 * Adds a projection (folding) region to this context. The created annotation / position
164 * pair will be added to the {@link ProjectionAnnotationModel} of the
165 * {@link ProjectionViewer} of the editor.
166 *
167 * @param annotation the annotation to add
168 * @param position the corresponding position
169 */
170 public void addProjectionRange(JavaProjectionAnnotation annotation, Position position) {
171 fMap.put(annotation, position);
172 }
173
174 /**
175 * Returns <code>true</code> if header comments should be collapsed.
176 *
177 * @return <code>true</code> if header comments should be collapsed
178 */
179 public boolean collapseHeaderComments() {
180 return fAllowCollapsing && fCollapseHeaderComments;
181 }
182
183 /**
184 * Returns <code>true</code> if import containers should be collapsed.
185 *
186 * @return <code>true</code> if import containers should be collapsed
187 */
188 public boolean collapseImportContainer() {
189 return fAllowCollapsing && fCollapseImportContainer;
190 }
191
192 /**
193 * Returns <code>true</code> if inner types should be collapsed.
194 *
195 * @return <code>true</code> if inner types should be collapsed
196 */
197 public boolean collapseInnerTypes() {
198 return fAllowCollapsing && fCollapseInnerTypes;
199 }
200
201 /**
202 * Returns <code>true</code> if javadoc comments should be collapsed.
203 *
204 * @return <code>true</code> if javadoc comments should be collapsed
205 */
206 public boolean collapseJavadoc() {
207 return fAllowCollapsing && fCollapseJavadoc;
208 }
209
210 /**
211 * Returns <code>true</code> if methods should be collapsed.
212 *
213 * @return <code>true</code> if methods should be collapsed
214 */
215 public boolean collapseMembers() {
216 return fAllowCollapsing && fCollapseMembers;
217 }
218
219 public void generated_100928497251475465(DefaultJavaFoldingStructureProvider defaultjavafoldingstructureprovider, IParent parent, String source) throws JavaModelException {
220 getScanner().setSource(source.toCharArray());
221 defaultjavafoldingstructureprovider.computeFoldingStructure(parent.getChildren(), this);
222 }
223
224 public void generated_323515713912890140(IJavaElement element, DefaultJavaFoldingStructureProvider defaultjavafoldingstructureprovider, boolean collapse, boolean collapseCode) {
225 switch (element.getElementType()) {
226
227 case IJavaElement.IMPORT_CONTAINER:
228 collapse= collapseImportContainer();
229 break;
230 case IJavaElement.TYPE:
231 collapseCode= defaultjavafoldingstructureprovider.isInnerType((IType) element) && !defaultjavafoldingstructureprovider.isAnonymousEnum((IType) element);
232 collapse= collapseInnerTypes() && collapseCode;
233 break;
234 case IJavaElement.METHOD:
235 case IJavaElement.FIELD:
236 case IJavaElement.INITIALIZER:
237 collapse= collapseMembers();
238 break;
239 default:
240 return;
241 }
242
243 IRegion[] regions= defaultjavafoldingstructureprovider.computeProjectionRanges((ISourceReference) element, this);
244 if (regions.length > 0) {
245 // comments
246 for (int i= 0; i < regions.length - 1; i++) {
247 IRegion normalized= defaultjavafoldingstructureprovider.alignRegion(regions[i], this);
248 if (normalized != null) {
249 Position position= defaultjavafoldingstructureprovider.createCommentPosition(normalized);
250 if (position != null) {
251 boolean commentCollapse;
252 if (i == 0 && (regions.length > 2 || hasHeaderComment()) && element == getFirstType()) {
253 commentCollapse= collapseHeaderComments();
254 } else {
255 commentCollapse= collapseJavadoc();
256 }
257 addProjectionRange(new JavaProjectionAnnotation(commentCollapse, element, true), position);
258 }
259 }
260 }
261 // code
262 if (collapseCode) {
263 IRegion normalized= defaultjavafoldingstructureprovider.alignRegion(regions[regions.length - 1], this);
264 if (normalized != null) {
265 Position position= element instanceof IMember ? defaultjavafoldingstructureprovider.createMemberPosition(normalized, (IMember) element) : defaultjavafoldingstructureprovider.createCommentPosition(normalized);
266 if (position != null)
267 addProjectionRange(new JavaProjectionAnnotation(collapse, element, false), position);
268 }
269 }
270 }
271 }
272
273 public void generated_8988951760493717037(ISourceReference reference, DefaultJavaFoldingStructureProvider defaultjavafoldingstructureprovider, List<IRegion> regions) throws JavaModelException {
274 setFirstType((IType) reference);
275 IRegion headerComment= defaultjavafoldingstructureprovider.computeHeaderComment(this);
276 if (headerComment != null) {
277 regions.add(headerComment);
278 setHasHeaderComment();
279 }
280 }
281
282 public IRegion generated_3843785453625915794() throws JavaModelException {
283 ISourceRange range= getFirstType().getSourceRange();
284 if (range == null)
285 return null;
286 int start= 0;
287 int end= range.getOffset();
288
289
290 /* code adapted from CommentFormattingStrategy:
291 * scan the header content up to the first type. Once a comment is
292 * found, accumulate any additional comments up to the stop condition.
293 * The stop condition is reaching a package declaration, import container,
294 * or the end of the input.
295 */
296 IScanner scanner= getScanner();
297 scanner.resetTo(start, end);
298
299 int headerStart= -1;
300 int headerEnd= -1;
301 try {
302 boolean foundComment= false;
303 int terminal= scanner.getNextToken();
304 while (terminal != ITerminalSymbols.TokenNameEOF && !(terminal == ITerminalSymbols.TokenNameclass || terminal == ITerminalSymbols.TokenNameinterface || terminal == ITerminalSymbols.TokenNameenum || (foundComment && (terminal == ITerminalSymbols.TokenNameimport || terminal == ITerminalSymbols.TokenNamepackage)))) {
305
306 if (terminal == ITerminalSymbols.TokenNameCOMMENT_JAVADOC || terminal == ITerminalSymbols.TokenNameCOMMENT_BLOCK || terminal == ITerminalSymbols.TokenNameCOMMENT_LINE) {
307 if (!foundComment)
308 headerStart= scanner.getCurrentTokenStartPosition();
309 headerEnd= scanner.getCurrentTokenEndPosition();
310 foundComment= true;
311 }
312 terminal= scanner.getNextToken();
313 }
314
315
316 } catch (InvalidInputException ex) {
317 return null;
318 }
319
320 if (headerEnd != -1) {
321 return new Region(headerStart, headerEnd - headerStart);
322 }
323 return null;
324 }
325 }
326
327 /**
328 * A {@link ProjectionAnnotation} for java code.
329 */
330 protected static final class JavaProjectionAnnotation extends ProjectionAnnotation {
331
332 private IJavaElement fJavaElement;
333 private boolean fIsComment;
334
335 /**
336 * Creates a new projection annotation.
337 *
338 * @param isCollapsed <code>true</code> to set the initial state to collapsed,
339 * <code>false</code> to set it to expanded
340 * @param element the java element this annotation refers to
341 * @param isComment <code>true</code> for a foldable comment, <code>false</code> for a
342 * foldable code element
343 */
344 public JavaProjectionAnnotation(boolean isCollapsed, IJavaElement element, boolean isComment) {
345 super(isCollapsed);
346 fJavaElement= element;
347 fIsComment= isComment;
348 }
349
350 IJavaElement getElement() {
351 return fJavaElement;
352 }
353
354 void setElement(IJavaElement element) {
355 fJavaElement= element;
356 }
357
358 boolean isComment() {
359 return fIsComment;
360 }
361
362 void setIsComment(boolean isComment) {
363 fIsComment= isComment;
364 }
365
366 /*
367 * @see java.lang.Object#toString()
368 */
369 @Override
370 public String toString() {
371 return "JavaProjectionAnnotation:\n" + //$NON-NLS-1$
372 "\telement: \t"+ fJavaElement.toString() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
373 "\tcollapsed: \t" + isCollapsed() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
374 "\tcomment: \t" + isComment() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
375 }
376
377 public void generated_7723998622636648457(JavaProjectionAnnotation newAnnotation) {
378 if (newAnnotation.isCollapsed())
379 markCollapsed();
380 else
381 markExpanded();
382 }
383
384 public Position generated_7272779290687362405(Map<JavaProjectionAnnotation, Position> positionMap, FoldingStructureComputationContext ctx) {
385 Position position= positionMap == null ? ctx.getModel().getPosition(this) : positionMap.get(this);
386 return position;
387 }
388
389 public void generated_6809230897511527110(Filter filter, boolean expand, List<JavaProjectionAnnotation> modified) {
390 if (expand == isCollapsed() && filter.match(this)) {
391 if (expand)
392 markExpanded();
393 else
394 markCollapsed();
395 modified.add(this);
396 }
397 }
398 }
399
400
401 static final class Tuple {
402 JavaProjectionAnnotation annotation;
403 Position position;
404 Tuple(JavaProjectionAnnotation annotation, Position position) {
405 this.annotation= annotation;
406 this.position= position;
407 }
408 public void generated_5894515189644979979(List<JavaProjectionAnnotation> newDeletions, List<JavaProjectionAnnotation> newChanges, Iterator<JavaProjectionAnnotation> deletionIterator, JavaProjectionAnnotation deleted, Position deletedPosition, DefaultJavaFoldingStructureProvider defaultjavafoldingstructureprovider, boolean addToDeletions, IJavaElement element) {
409 deletedPosition.setLength(position.getLength());
410 if (deletedPosition instanceof JavaElementPosition && element instanceof IMember) {
411 JavaElementPosition jep= (JavaElementPosition) deletedPosition;
412 jep.setMember((IMember) element);
413 }
414
415 deletionIterator.remove();
416 newChanges.add(deleted);
417
418 if (addToDeletions)
419 newDeletions.add(annotation);
420 }
421 public int generated_4432417452402132400(Tuple o1, Comparator<Tuple> arg) {
422 return o1.position.getOffset() - position.getOffset();
423 }
424 }
425
426 /**
427 * Filter for annotations.
428 */
429 static interface Filter {
430 boolean match(JavaProjectionAnnotation annotation);
431 }
432
433 /**
434 * Matches comments.
435 */
436 private static final class CommentFilter implements Filter {
437 public boolean match(JavaProjectionAnnotation annotation) {
438 if (annotation.isComment() && !annotation.isMarkedDeleted()) {
439 return true;
440 }
441 return false;
442 }
443 }
444
445 /**
446 * Matches members.
447 */
448 private static final class MemberFilter implements Filter {
449 public boolean match(JavaProjectionAnnotation annotation) {
450 if (!annotation.isComment() && !annotation.isMarkedDeleted()) {
451 IJavaElement element= annotation.getElement();
452 if (element instanceof IMember) {
453 if (element.getElementType() != IJavaElement.TYPE || ((IMember) element).getDeclaringType() != null) {
454 return true;
455 }
456 }
457 }
458 return false;
459 }
460 }
461
462 /**
463 * Matches java elements contained in a certain set.
464 */
465 private static final class JavaElementSetFilter implements Filter {
466 private final Set<? extends IJavaElement> fSet;
467 private final boolean fMatchCollapsed;
468
469 private JavaElementSetFilter(Set<? extends IJavaElement> set, boolean matchCollapsed) {
470 fSet= set;
471 fMatchCollapsed= matchCollapsed;
472 }
473
474 public boolean match(JavaProjectionAnnotation annotation) {
475 boolean stateMatch= fMatchCollapsed == annotation.isCollapsed();
476 if (stateMatch && !annotation.isComment() && !annotation.isMarkedDeleted()) {
477 IJavaElement element= annotation.getElement();
478 if (fSet.contains(element)) {
479 return true;
480 }
481 }
482 return false;
483 }
484 }
485
486 public class ElementChangedListener implements IElementChangedListener {
487
488 /*
489 * @see org.eclipse.jdt.core.IElementChangedListener#elementChanged(org.eclipse.jdt.core.ElementChangedEvent)
490 */
491 public void elementChanged(ElementChangedEvent e) {
492 IJavaElementDelta delta= findElement(fInput, e.getDelta());
493 if (delta != null && (delta.getFlags() & (IJavaElementDelta.F_CONTENT | IJavaElementDelta.F_CHILDREN)) != 0) {
494
495 if (shouldIgnoreDelta(e.getDelta().getCompilationUnitAST(), delta))
496 return;
497
498 fUpdatingCount++;
499 try {
500 update(createContext(false));
501 } finally {
502 fUpdatingCount--;
503 }
504 }
505 }
506
507 /**
508 * Ignore the delta if there are errors on the caret line.
509 * <p>
510 * We don't ignore the delta if an import is added and the
511 * caret isn't inside the import container.
512 * </p>
513 *
514 * @param ast the compilation unit AST
515 * @param delta the Java element delta for the given AST element
516 * @return <code>true</code> if the delta should be ignored
517 * @since 3.3
518 */
519 private boolean shouldIgnoreDelta(CompilationUnit ast, IJavaElementDelta delta) {
520 if (ast == null)
521 return false; // can't compute
522
523 IDocument document= getDocument();
524 if (document == null)
525 return false; // can't compute
526
527 JavaEditor editor= fEditor;
528 if (editor == null || editor.getCachedSelectedRange() == null)
529 return false; // can't compute
530
531 return editor.generated_3091946843584142038(ast, delta, document, ElementChangedListener.this);
532 }
533
534 private IJavaElementDelta findElement(IJavaElement target, IJavaElementDelta delta) {
535
536 if (delta == null || target == null)
537 return null;
538
539 IJavaElement element= delta.getElement();
540
541 if (element.getElementType() > IJavaElement.CLASS_FILE)
542 return null;
543
544 if (target.equals(element))
545 return delta;
546
547 IJavaElementDelta[] children= delta.getAffectedChildren();
548
549 for (int i= 0; i < children.length; i++) {
550 IJavaElementDelta d= findElement(target, children[i]);
551 if (d != null)
552 return d;
553 }
554
555 return null;
556 }
557 }
558
559 /**
560 * Projection position that will return two foldable regions: one folding away
561 * the region from after the '/**' to the beginning of the content, the other
562 * from after the first content line until after the comment.
563 */
564 private static final class CommentPosition extends Position implements IProjectionPosition {
565 CommentPosition(int offset, int length) {
566 super(offset, length);
567 }
568
569 /*
570 * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeFoldingRegions(org.eclipse.jface.text.IDocument)
571 */
572 public IRegion[] computeProjectionRegions(IDocument document) throws BadLocationException {
573 DocumentCharacterIterator sequence= new DocumentCharacterIterator(document, offset, offset + length);
574 int prefixEnd= 0;
575 int contentStart= findFirstContent(sequence, prefixEnd);
576
577 int firstLine= document.getLineOfOffset(offset + prefixEnd);
578 int captionLine= document.getLineOfOffset(offset + contentStart);
579 int lastLine= document.getLineOfOffset(offset + length);
580
581 Assert.isTrue(firstLine <= captionLine, "first folded line is greater than the caption line"); //$NON-NLS-1$
582 Assert.isTrue(captionLine <= lastLine, "caption line is greater than the last folded line"); //$NON-NLS-1$
583
584 IRegion preRegion;
585 if (firstLine < captionLine) {
586// preRegion= new Region(offset + prefixEnd, contentStart - prefixEnd);
587 int preOffset= document.getLineOffset(firstLine);
588 IRegion preEndLineInfo= document.getLineInformation(captionLine);
589 int preEnd= preEndLineInfo.getOffset();
590 preRegion= new Region(preOffset, preEnd - preOffset);
591 } else {
592 preRegion= null;
593 }
594
595 if (captionLine < lastLine) {
596 int postOffset= document.getLineOffset(captionLine + 1);
597 int postLength= offset + length - postOffset;
598 if (postLength > 0) {
599 IRegion postRegion= new Region(postOffset, postLength);
600 if (preRegion == null)
601 return new IRegion[] { postRegion };
602 return new IRegion[] { preRegion, postRegion };
603 }
604 }
605
606 if (preRegion != null)
607 return new IRegion[] { preRegion };
608
609 return null;
610 }
611
612 /**
613 * Finds the offset of the first identifier part within <code>content</code>.
614 * Returns 0 if none is found.
615 *
616 * @param content the content to search
617 * @param prefixEnd the end of the prefix
618 * @return the first index of a unicode identifier part, or zero if none can
619 * be found
620 */
621 private int findFirstContent(final CharSequence content, int prefixEnd) {
622 int lenght= content.length();
623 for (int i= prefixEnd; i < lenght; i++) {
624 if (Character.isUnicodeIdentifierPart(content.charAt(i)))
625 return i;
626 }
627 return 0;
628 }
629
630// /**
631// * Finds the offset of the first identifier part within <code>content</code>.
632// * Returns 0 if none is found.
633// *
634// * @param content the content to search
635// * @return the first index of a unicode identifier part, or zero if none can
636// * be found
637// */
638// private int findPrefixEnd(final CharSequence content) {
639// // return the index after the leading '/*' or '/**'
640// int len= content.length();
641// int i= 0;
642// while (i < len && isWhiteSpace(content.charAt(i)))
643// i++;
644// if (len >= i + 2 && content.charAt(i) == '/' && content.charAt(i + 1) == '*')
645// if (len >= i + 3 && content.charAt(i + 2) == '*')
646// return i + 3;
647// else
648// return i + 2;
649// else
650// return i;
651// }
652//
653// private boolean isWhiteSpace(char c) {
654// return c == ' ' || c == '\t';
655// }
656
657 /*
658 * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeCaptionOffset(org.eclipse.jface.text.IDocument)
659 */
660 public int computeCaptionOffset(IDocument document) throws BadLocationException {
661 DocumentCharacterIterator sequence= new DocumentCharacterIterator(document, offset, offset + length);
662 return findFirstContent(sequence, 0);
663 }
664 }
665
666 /**
667 * Projection position that will return two foldable regions: one folding away
668 * the lines before the one containing the simple name of the java element, one
669 * folding away any lines after the caption.
670 */
671 static final class JavaElementPosition extends Position implements IProjectionPosition {
672
673 private IMember fMember;
674
675 public JavaElementPosition(int offset, int length, IMember member) {
676 super(offset, length);
677 Assert.isNotNull(member);
678 fMember= member;
679 }
680
681 public void setMember(IMember member) {
682 Assert.isNotNull(member);
683 fMember= member;
684 }
685
686 /*
687 * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeFoldingRegions(org.eclipse.jface.text.IDocument)
688 */
689 public IRegion[] computeProjectionRegions(IDocument document) throws BadLocationException {
690 int nameStart= offset;
691 try {
692 /* The member's name range may not be correct. However,
693 * reconciling would trigger another element delta which would
694 * lead to reentrant situations. Therefore, we optimistically
695 * assume that the name range is correct, but double check the
696 * received lines below. */
697 ISourceRange nameRange= fMember.getNameRange();
698 if (nameRange != null)
699 nameStart= nameRange.getOffset();
700
701 } catch (JavaModelException e) {
702 // ignore and use default
703 }
704
705 int firstLine= document.getLineOfOffset(offset);
706 int captionLine= document.getLineOfOffset(nameStart);
707 int lastLine= document.getLineOfOffset(offset + length);
708
709 /* see comment above - adjust the caption line to be inside the
710 * entire folded region, and rely on later element deltas to correct
711 * the name range. */
712 if (captionLine < firstLine)
713 captionLine= firstLine;
714 if (captionLine > lastLine)
715 captionLine= lastLine;
716
717 IRegion preRegion;
718 if (firstLine < captionLine) {
719 int preOffset= document.getLineOffset(firstLine);
720 IRegion preEndLineInfo= document.getLineInformation(captionLine);
721 int preEnd= preEndLineInfo.getOffset();
722 preRegion= new Region(preOffset, preEnd - preOffset);
723 } else {
724 preRegion= null;
725 }
726
727 if (captionLine < lastLine) {
728 int postOffset= document.getLineOffset(captionLine + 1);
729 int postLength= offset + length - postOffset;
730 if (postLength > 0) {
731 IRegion postRegion= new Region(postOffset, postLength);
732 if (preRegion == null)
733 return new IRegion[] { postRegion };
734 return new IRegion[] { preRegion, postRegion };
735 }
736 }
737
738 if (preRegion != null)
739 return new IRegion[] { preRegion };
740
741 return null;
742 }
743
744 /*
745 * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeCaptionOffset(org.eclipse.jface.text.IDocument)
746 */
747 public int computeCaptionOffset(IDocument document) throws BadLocationException {
748 int nameStart= offset;
749 try {
750 // need a reconcile here?
751 ISourceRange nameRange= fMember.getNameRange();
752 if (nameRange != null)
753 nameStart= nameRange.getOffset();
754 } catch (JavaModelException e) {
755 // ignore and use default
756 }
757
758 return nameStart - offset;
759 }
760
761 }
762
763 /**
764 * Internal projection listener.
765 */
766 private final class ProjectionListener implements IProjectionListener {
767 private ProjectionViewer fViewer;
768
769 /**
770 * Registers the listener with the viewer.
771 *
772 * @param viewer the viewer to register a listener with
773 */
774 public ProjectionListener(ProjectionViewer viewer) {
775 Assert.isLegal(viewer != null);
776 fViewer= viewer;
777 fViewer.addProjectionListener(this);
778 }
779
780 /**
781 * Disposes of this listener and removes the projection listener from the viewer.
782 */
783 public void dispose() {
784 if (fViewer != null) {
785 fViewer.removeProjectionListener(this);
786 fViewer= null;
787 }
788 }
789
790 /*
791 * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionEnabled()
792 */
793 public void projectionEnabled() {
794 handleProjectionEnabled();
795 }
796
797 /*
798 * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionDisabled()
799 */
800 public void projectionDisabled() {
801 handleProjectionDisabled();
802 }
803 }
804
805 /* context and listeners */
806 private JavaEditor fEditor;
807 private ProjectionListener fProjectionListener;
808 private IJavaElement fInput;
809 private IElementChangedListener fElementListener;
810
811 /* preferences */
812 private boolean fCollapseJavadoc= false;
813 private boolean fCollapseImportContainer= true;
814 private boolean fCollapseInnerTypes= true;
815 private boolean fCollapseMembers= false;
816 private boolean fCollapseHeaderComments= true;
817
818 /* filters */
819 /** Member filter, matches nested members (but not top-level types). */
820 private final Filter fMemberFilter = new MemberFilter();
821 /** Comment filter, matches comments. */
822 private final Filter fCommentFilter = new CommentFilter();
823
824 /**
825 * Reusable scanner.
826 * @since 3.3
827 */
828 private IScanner fSharedScanner= ToolFactory.createScanner(true, false, false, false);
829
830 private volatile int fUpdatingCount= 0;
831
832 /**
833 * Creates a new folding provider. It must be
834 * {@link #install(ITextEditor, ProjectionViewer) installed} on an editor/viewer pair before it
835 * can be used, and {@link #uninstall() uninstalled} when not used any longer.
836 * <p>
837 * The projection state may be reset by calling {@link #initialize()}.
838 * </p>
839 */
840 public DefaultJavaFoldingStructureProvider() {
841 }
842
843 /**
844 * {@inheritDoc}
845 * <p>
846 * Subclasses may extend.
847 * </p>
848 *
849 * @param editor {@inheritDoc}
850 * @param viewer {@inheritDoc}
851 */
852 public void install(ITextEditor editor, ProjectionViewer viewer) {
853 Assert.isLegal(editor != null);
854 Assert.isLegal(viewer != null);
855
856 internalUninstall();
857
858 if (editor instanceof JavaEditor) {
859 fProjectionListener= new ProjectionListener(viewer);
860 fEditor= (JavaEditor)editor;
861 }
862 }
863
864 /**
865 * {@inheritDoc}
866 * <p>
867 * Subclasses may extend.
868 * </p>
869 */
870 public void uninstall() {
871 internalUninstall();
872 }
873
874 /**
875 * Internal implementation of {@link #uninstall()}.
876 */
877 private void internalUninstall() {
878 if (isInstalled()) {
879 handleProjectionDisabled();
880 fProjectionListener.dispose();
881 fProjectionListener= null;
882 fEditor= null;
883 }
884 }
885
886 /**
887 * Returns <code>true</code> if the provider is installed, <code>false</code> otherwise.
888 *
889 * @return <code>true</code> if the provider is installed, <code>false</code> otherwise
890 */
891 protected final boolean isInstalled() {
892 return fEditor != null;
893 }
894
895 /**
896 * Called whenever projection is enabled, for example when the viewer issues a
897 * {@link IProjectionListener#projectionEnabled() projectionEnabled} message. When the provider
898 * is already enabled when this method is called, it is first
899 * {@link #handleProjectionDisabled() disabled}.
900 * <p>
901 * Subclasses may extend.
902 * </p>
903 */
904 protected void handleProjectionEnabled() {
905 // http://home.ott.oti.com/teams/wswb/anon/out/vms/index.html
906 // projectionEnabled messages are not always paired with projectionDisabled
907 // i.e. multiple enabled messages may be sent out.
908 // we have to make sure that we disable first when getting an enable
909 // message.
910 handleProjectionDisabled();
911
912 if (isInstalled()) {
913 initialize();
914 fElementListener= new ElementChangedListener();
915 JavaCore.addElementChangedListener(fElementListener);
916 }
917 }
918
919 /**
920 * Called whenever projection is disabled, for example when the provider is
921 * {@link #uninstall() uninstalled}, when the viewer issues a
922 * {@link IProjectionListener#projectionDisabled() projectionDisabled} message and before
923 * {@link #handleProjectionEnabled() enabling} the provider. Implementations must be prepared to
924 * handle multiple calls to this method even if the provider is already disabled.
925 * <p>
926 * Subclasses may extend.
927 * </p>
928 */
929 protected void handleProjectionDisabled() {
930 if (fElementListener != null) {
931 JavaCore.removeElementChangedListener(fElementListener);
932 fElementListener= null;
933 }
934 }
935
936 /*
937 * @see org.eclipse.jdt.ui.text.folding.IJavaFoldingStructureProvider#initialize()
938 */
939 public final void initialize() {
940 fUpdatingCount++;
941 try {
942 update(createInitialContext());
943 } finally {
944 fUpdatingCount--;
945 }
946 }
947
948 private FoldingStructureComputationContext createInitialContext() {
949 initializePreferences();
950 fInput= getInputElement();
951 if (fInput == null)
952 return null;
953
954 return createContext(true);
955 }
956
957 private FoldingStructureComputationContext createContext(boolean allowCollapse) {
958 if (!isInstalled())
959 return null;
960 ProjectionAnnotationModel model= getModel();
961 if (model == null)
962 return null;
963 IDocument doc= getDocument();
964 if (doc == null)
965 return null;
966
967 IScanner scanner= null;
968 if (fUpdatingCount == 1)
969 scanner= fSharedScanner; // reuse scanner
970
971 return new FoldingStructureComputationContext(doc, model, allowCollapse, scanner);
972 }
973
974 private IJavaElement getInputElement() {
975 if (fEditor == null)
976 return null;
977 return EditorUtility.getEditorInputJavaElement(fEditor, false);
978 }
979
980 private void initializePreferences() {
981 IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore();
982 fCollapseInnerTypes= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_INNERTYPES);
983 fCollapseImportContainer= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_IMPORTS);
984 fCollapseJavadoc= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_JAVADOC);
985 fCollapseMembers= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_METHODS);
986 fCollapseHeaderComments= store.getBoolean(PreferenceConstants.EDITOR_FOLDING_HEADERS);
987 }
988
989 private void update(FoldingStructureComputationContext ctx) {
990 if (ctx == null)
991 return;
992
993 Map<JavaProjectionAnnotation, Position> additions= new HashMap<JavaProjectionAnnotation, Position>();
994 List<JavaProjectionAnnotation> deletions= new ArrayList<JavaProjectionAnnotation>();
995 List<JavaProjectionAnnotation> updates= new ArrayList<JavaProjectionAnnotation>();
996
997 computeFoldingStructure(ctx);
998 Map<JavaProjectionAnnotation, Position> newStructure= ctx.fMap;
999 Map<IJavaElement, List<Tuple>> oldStructure= computeCurrentStructure(ctx);
1000
1001 Iterator<JavaProjectionAnnotation> e= newStructure.keySet().iterator();
1002 while (e.hasNext()) {
1003 JavaProjectionAnnotation newAnnotation= e.next();
1004 Position newPosition= newStructure.get(newAnnotation);
1005
1006 IJavaElement element= newAnnotation.getElement();
1007 /*
1008 * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=130472 and
1009 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=127445 In the presence of syntax
1010 * errors, anonymous types may have a source range offset of 0. When such a situation is
1011 * encountered, we ignore the proposed folding range: if no corresponding folding range
1012 * exists, it is silently ignored; if there *is* a matching folding range, we ignore the
1013 * position update and keep the old range, in order to keep the folding structure
1014 * stable.
1015 */
1016 boolean isMalformedAnonymousType= newPosition.getOffset() == 0 && element.getElementType() == IJavaElement.TYPE && isInnerType((IType) element);
1017 List<Tuple> annotations= oldStructure.get(element);
1018 if (annotations == null) {
1019 if (!isMalformedAnonymousType)
1020 additions.put(newAnnotation, newPosition);
1021 } else {
1022 Iterator<Tuple> x= annotations.iterator();
1023 boolean matched= false;
1024 while (x.hasNext()) {
1025 Tuple tuple= x.next();
1026 JavaProjectionAnnotation existingAnnotation= tuple.annotation;
1027 Position existingPosition= tuple.position;
1028 if (newAnnotation.isComment() == existingAnnotation.isComment()) {
1029 boolean updateCollapsedState= ctx.allowCollapsing() && existingAnnotation.isCollapsed() != newAnnotation.isCollapsed();
1030 if (!isMalformedAnonymousType && existingPosition != null && (!newPosition.equals(existingPosition) || updateCollapsedState)) {
1031 existingPosition.setOffset(newPosition.getOffset());
1032 existingPosition.setLength(newPosition.getLength());
1033 if (updateCollapsedState)
1034 existingAnnotation.generated_7723998622636648457(newAnnotation);
1035 updates.add(existingAnnotation);
1036 }
1037 matched= true;
1038 x.remove();
1039 break;
1040 }
1041 }
1042 if (!matched)
1043 additions.put(newAnnotation, newPosition);
1044
1045 if (annotations.isEmpty())
1046 oldStructure.remove(element);
1047 }
1048 }
1049
1050 Iterator<List<Tuple>> iter= oldStructure.values().iterator();
1051 while (iter.hasNext()) {
1052 List<Tuple> list= iter.next();
1053 int size= list.size();
1054 for (int i= 0; i < size; i++)
1055 deletions.add(list.get(i).annotation);
1056 }
1057
1058 match(deletions, additions, updates, ctx);
1059
1060 Annotation[] deletedArray= deletions.toArray(new Annotation[deletions.size()]);
1061 Annotation[] changedArray= updates.toArray(new Annotation[updates.size()]);
1062 ctx.getModel().modifyAnnotations(deletedArray, additions, changedArray);
1063
1064 ctx.fScanner.setSource(null);
1065 }
1066
1067 private void computeFoldingStructure(FoldingStructureComputationContext ctx) {
1068 IParent parent= (IParent) fInput;
1069 try {
1070 if (!(fInput instanceof ISourceReference))
1071 return;
1072 String source= ((ISourceReference)fInput).getSource();
1073 if (source == null)
1074 return;
1075
1076 ctx.generated_100928497251475465(this, parent, source);
1077 } catch (JavaModelException x) {
1078 }
1079 }
1080
1081 private void computeFoldingStructure(IJavaElement[] elements, FoldingStructureComputationContext ctx) throws JavaModelException {
1082 for (int i= 0; i < elements.length; i++) {
1083 IJavaElement element= elements[i];
1084
1085 computeFoldingStructure(element, ctx);
1086
1087 if (element instanceof IParent) {
1088 IParent parent= (IParent) element;
1089 computeFoldingStructure(parent.getChildren(), ctx);
1090 }
1091 }
1092 }
1093
1094 /**
1095 * Computes the folding structure for a given {@link IJavaElement java element}. Computed
1096 * projection annotations are
1097 * {@link DefaultJavaFoldingStructureProvider.FoldingStructureComputationContext#addProjectionRange(DefaultJavaFoldingStructureProvider.JavaProjectionAnnotation, Position) added}
1098 * to the computation context.
1099 * <p>
1100 * Subclasses may extend or replace. The default implementation creates projection annotations
1101 * for the following elements:
1102 * <ul>
1103 * <li>true members (not for top-level types)</li>
1104 * <li>the javadoc comments of any member</li>
1105 * <li>header comments (javadoc or multi-line comments appearing before the first type's
1106 * javadoc or before the package or import declarations).</li>
1107 * </ul>
1108 * </p>
1109 *
1110 * @param element the java element to compute the folding structure for
1111 * @param ctx the computation context
1112 */
1113 protected void computeFoldingStructure(IJavaElement element, FoldingStructureComputationContext ctx) {
1114
1115 boolean collapse= false;
1116 boolean collapseCode= true;
1117 ctx.generated_323515713912890140(element, this, collapse, collapseCode);
1118 }
1119
1120 /**
1121 * Returns <code>true</code> if <code>type</code> is an anonymous enum declaration,
1122 * <code>false</code> otherwise. See also https://bugs.eclipse.org/bugs/show_bug.cgi?id=143276
1123 *
1124 * @param type the type to test
1125 * @return <code>true</code> if <code>type</code> is an anonymous enum declaration
1126 * @since 3.3
1127 */
1128 private boolean isAnonymousEnum(IType type) {
1129 try {
1130 return type.isEnum() && type.isAnonymous();
1131 } catch (JavaModelException x) {
1132 return false; // optimistically
1133 }
1134 }
1135
1136 /**
1137 * Returns <code>true</code> if <code>type</code> is not a top-level type, <code>false</code> if it is.
1138 *
1139 * @param type the type to test
1140 * @return <code>true</code> if <code>type</code> is an inner type
1141 */
1142 private boolean isInnerType(IType type) {
1143 return type.getDeclaringType() != null;
1144 }
1145
1146 /**
1147 * Computes the projection ranges for a given <code>ISourceReference</code>. More than one
1148 * range or none at all may be returned. If there are no foldable regions, an empty array is
1149 * returned.
1150 * <p>
1151 * The last region in the returned array (if not empty) describes the region for the java
1152 * element that implements the source reference. Any preceding regions describe javadoc comments
1153 * of that java element.
1154 * </p>
1155 *
1156 * @param reference a java element that is a source reference
1157 * @param ctx the folding context
1158 * @return the regions to be folded
1159 */
1160 protected final IRegion[] computeProjectionRanges(ISourceReference reference, FoldingStructureComputationContext ctx) {
1161 try {
1162 ISourceRange range= reference.getSourceRange();
1163 if (!SourceRange.isAvailable(range))
1164 return new IRegion[0];
1165
1166 String contents= reference.getSource();
1167 if (contents == null)
1168 return new IRegion[0];
1169
1170 List<IRegion> regions= new ArrayList<IRegion>();
1171 if (!ctx.hasFirstType() && reference instanceof IType) {
1172 ctx.generated_8988951760493717037(reference, this, regions);
1173 }
1174
1175 final int shift= range.getOffset();
1176 IScanner scanner= ctx.getScanner();
1177 scanner.resetTo(shift, shift + range.getLength());
1178
1179 int start= shift;
1180 while (true) {
1181
1182 int token= scanner.getNextToken();
1183 start= scanner.getCurrentTokenStartPosition();
1184
1185 switch (token) {
1186 case ITerminalSymbols.TokenNameCOMMENT_JAVADOC:
1187 case ITerminalSymbols.TokenNameCOMMENT_BLOCK: {
1188 int end= scanner.getCurrentTokenEndPosition() + 1;
1189 regions.add(new Region(start, end - start));
1190 continue;
1191 }
1192 case ITerminalSymbols.TokenNameCOMMENT_LINE:
1193 continue;
1194 }
1195
1196 break;
1197 }
1198
1199 regions.add(new Region(start, shift + range.getLength() - start));
1200
1201 IRegion[] result= new IRegion[regions.size()];
1202 regions.toArray(result);
1203 return result;
1204 } catch (JavaModelException e) {
1205 } catch (InvalidInputException e) {
1206 }
1207
1208 return new IRegion[0];
1209 }
1210
1211 private IRegion computeHeaderComment(FoldingStructureComputationContext ctx) throws JavaModelException {
1212 // search at most up to the first type
1213 return ctx.generated_3843785453625915794();
1214 }
1215
1216 /**
1217 * Creates a comment folding position from an
1218 * {@link #alignRegion(IRegion, DefaultJavaFoldingStructureProvider.FoldingStructureComputationContext) aligned}
1219 * region.
1220 *
1221 * @param aligned an aligned region
1222 * @return a folding position corresponding to <code>aligned</code>
1223 */
1224 protected final Position createCommentPosition(IRegion aligned) {
1225 return new CommentPosition(aligned.getOffset(), aligned.getLength());
1226 }
1227
1228 /**
1229 * Creates a folding position that remembers its member from an
1230 * {@link #alignRegion(IRegion, DefaultJavaFoldingStructureProvider.FoldingStructureComputationContext) aligned}
1231 * region.
1232 *
1233 * @param aligned an aligned region
1234 * @param member the member to remember
1235 * @return a folding position corresponding to <code>aligned</code>
1236 */
1237 protected final Position createMemberPosition(IRegion aligned, IMember member) {
1238 return new JavaElementPosition(aligned.getOffset(), aligned.getLength(), member);
1239 }
1240
1241 /**
1242 * Aligns <code>region</code> to start and end at a line offset. The region's start is
1243 * decreased to the next line offset, and the end offset increased to the next line start or the
1244 * end of the document. <code>null</code> is returned if <code>region</code> is
1245 * <code>null</code> itself or does not comprise at least one line delimiter, as a single line
1246 * cannot be folded.
1247 *
1248 * @param region the region to align, may be <code>null</code>
1249 * @param ctx the folding context
1250 * @return a region equal or greater than <code>region</code> that is aligned with line
1251 * offsets, <code>null</code> if the region is too small to be foldable (e.g. covers
1252 * only one line)
1253 */
1254 protected final IRegion alignRegion(IRegion region, FoldingStructureComputationContext ctx) {
1255 if (region == null)
1256 return null;
1257
1258 IDocument document= ctx.getDocument();
1259
1260 try {
1261
1262 int start= document.getLineOfOffset(region.getOffset());
1263 int end= document.getLineOfOffset(region.getOffset() + region.getLength());
1264 if (start >= end)
1265 return null;
1266
1267 int offset= document.getLineOffset(start);
1268 int endOffset;
1269 if (document.getNumberOfLines() > end + 1)
1270 endOffset= document.getLineOffset(end + 1);
1271 else
1272 endOffset= document.getLineOffset(end) + document.getLineLength(end);
1273
1274 return new Region(offset, endOffset - offset);
1275
1276 } catch (BadLocationException x) {
1277 // concurrent modification
1278 return null;
1279 }
1280 }
1281
1282 private ProjectionAnnotationModel getModel() {
1283 return (ProjectionAnnotationModel) fEditor.getAdapter(ProjectionAnnotationModel.class);
1284 }
1285
1286 private IDocument getDocument() {
1287 JavaEditor editor= fEditor;
1288 if (editor == null)
1289 return null;
1290
1291 return editor.generated_4083872133110632577();
1292 }
1293
1294 /**
1295 * Matches deleted annotations to changed or added ones. A deleted
1296 * annotation/position tuple that has a matching addition / change
1297 * is updated and marked as changed. The matching tuple is not added
1298 * (for additions) or marked as deletion instead (for changes). The
1299 * result is that more annotations are changed and fewer get
1300 * deleted/re-added.
1301 *
1302 * @param deletions list with deleted annotations
1303 * @param additions map with position to annotation mappings
1304 * @param changes list with changed annotations
1305 * @param ctx the context
1306 */
1307 private void match(List<JavaProjectionAnnotation> deletions, Map<JavaProjectionAnnotation, Position> additions, List<JavaProjectionAnnotation> changes, FoldingStructureComputationContext ctx) {
1308 if (deletions.isEmpty() || (additions.isEmpty() && changes.isEmpty()))
1309 return;
1310
1311 List<JavaProjectionAnnotation> newDeletions= new ArrayList<JavaProjectionAnnotation>();
1312 List<JavaProjectionAnnotation> newChanges= new ArrayList<JavaProjectionAnnotation>();
1313
1314 Iterator<JavaProjectionAnnotation> deletionIterator= deletions.iterator();
1315 while (deletionIterator.hasNext()) {
1316 JavaProjectionAnnotation deleted= deletionIterator.next();
1317 Position deletedPosition= ctx.getModel().getPosition(deleted);
1318 if (deletedPosition == null)
1319 continue;
1320
1321 Tuple deletedTuple= new Tuple(deleted, deletedPosition);
1322
1323 Tuple match= findMatch(deletedTuple, changes, null, ctx);
1324 boolean addToDeletions= true;
1325 if (match == null) {
1326 match= findMatch(deletedTuple, additions.keySet(), additions, ctx);
1327 addToDeletions= false;
1328 }
1329
1330 if (match != null) {
1331 IJavaElement element= match.annotation.getElement();
1332 deleted.setElement(element);
1333 match.generated_5894515189644979979(newDeletions, newChanges, deletionIterator, deleted, deletedPosition, this, addToDeletions, element);
1334 }
1335 }
1336
1337 deletions.addAll(newDeletions);
1338 changes.addAll(newChanges);
1339 }
1340
1341 /**
1342 * Finds a match for <code>tuple</code> in a collection of
1343 * annotations. The positions for the
1344 * <code>JavaProjectionAnnotation</code> instances in
1345 * <code>annotations</code> can be found in the passed
1346 * <code>positionMap</code> or <code>fCachedModel</code> if
1347 * <code>positionMap</code> is <code>null</code>.
1348 * <p>
1349 * A tuple is said to match another if their annotations have the
1350 * same comment flag and their position offsets are equal.
1351 * </p>
1352 * <p>
1353 * If a match is found, the annotation gets removed from
1354 * <code>annotations</code>.
1355 * </p>
1356 *
1357 * @param tuple the tuple for which we want to find a match
1358 * @param annotations collection of
1359 * <code>JavaProjectionAnnotation</code>
1360 * @param positionMap a <code>Map&lt;Annotation, Position&gt;</code>
1361 * or <code>null</code>
1362 * @param ctx the context
1363 * @return a matching tuple or <code>null</code> for no match
1364 */
1365 private Tuple findMatch(Tuple tuple, Collection<JavaProjectionAnnotation> annotations, Map<JavaProjectionAnnotation, Position> positionMap, FoldingStructureComputationContext ctx) {
1366 Iterator<JavaProjectionAnnotation> it= annotations.iterator();
1367 while (it.hasNext()) {
1368 JavaProjectionAnnotation annotation= it.next();
1369 if (tuple.annotation.isComment() == annotation.isComment()) {
1370 Position position= annotation.generated_7272779290687362405(positionMap, ctx);
1371 if (position == null)
1372 continue;
1373
1374 if (tuple.position.getOffset() == position.getOffset()) {
1375 it.remove();
1376 return new Tuple(annotation, position);
1377 }
1378 }
1379 }
1380
1381 return null;
1382 }
1383
1384 private Map<IJavaElement, List<Tuple>> computeCurrentStructure(FoldingStructureComputationContext ctx) {
1385 Map<IJavaElement, List<Tuple>> map= new HashMap<IJavaElement, List<Tuple>>();
1386 ProjectionAnnotationModel model= ctx.getModel();
1387 Iterator<Annotation> e= model.getAnnotationIterator();
1388 while (e.hasNext()) {
1389 Object annotation= e.next();
1390 if (annotation instanceof JavaProjectionAnnotation) {
1391 JavaProjectionAnnotation java= (JavaProjectionAnnotation) annotation;
1392 Position position= model.getPosition(java);
1393 Assert.isNotNull(position);
1394 List<Tuple> list= map.get(java.getElement());
1395 if (list == null) {
1396 list= new ArrayList<Tuple>(2);
1397 map.put(java.getElement(), list);
1398 }
1399 list.add(new Tuple(java, position));
1400 }
1401 }
1402
1403 Comparator<Tuple> comparator= new Comparator<Tuple>() {
1404 public int compare(Tuple o1, Tuple o2) {
1405 return o2.generated_4432417452402132400(o1, this);
1406 }
1407 };
1408 for (Iterator<List<Tuple>> it= map.values().iterator(); it.hasNext();) {
1409 List<Tuple> list= it.next();
1410 Collections.sort(list, comparator);
1411 }
1412 return map;
1413 }
1414
1415 /*
1416 * @see IJavaFoldingStructureProviderExtension#collapseMembers()
1417 * @since 3.2
1418 */
1419 public final void collapseMembers() {
1420 modifyFiltered(fMemberFilter, false);
1421 }
1422
1423 /*
1424 * @see IJavaFoldingStructureProviderExtension#collapseComments()
1425 * @since 3.2
1426 */
1427 public final void collapseComments() {
1428 modifyFiltered(fCommentFilter, false);
1429 }
1430
1431 /*
1432 * @see org.eclipse.jdt.ui.text.folding.IJavaFoldingStructureProviderExtension#collapseElements(org.eclipse.jdt.core.IJavaElement[])
1433 */
1434 public final void collapseElements(IJavaElement[] elements) {
1435 Set<IJavaElement> set= new HashSet<IJavaElement>(Arrays.asList(elements));
1436 modifyFiltered(new JavaElementSetFilter(set, false), false);
1437 }
1438
1439 /*
1440 * @see org.eclipse.jdt.ui.text.folding.IJavaFoldingStructureProviderExtension#expandElements(org.eclipse.jdt.core.IJavaElement[])
1441 */
1442 public final void expandElements(IJavaElement[] elements) {
1443 Set<IJavaElement> set= new HashSet<IJavaElement>(Arrays.asList(elements));
1444 modifyFiltered(new JavaElementSetFilter(set, true), true);
1445 }
1446
1447 /**
1448 * Collapses or expands all annotations matched by the passed filter.
1449 *
1450 * @param filter the filter to use to select which annotations to collapse
1451 * @param expand <code>true</code> to expand the matched annotations, <code>false</code> to
1452 * collapse them
1453 */
1454 private void modifyFiltered(Filter filter, boolean expand) {
1455 if (!isInstalled())
1456 return;
1457
1458 ProjectionAnnotationModel model= getModel();
1459 if (model == null)
1460 return;
1461
1462 List<JavaProjectionAnnotation> modified= new ArrayList<JavaProjectionAnnotation>();
1463 Iterator<Annotation> iter= model.getAnnotationIterator();
1464 while (iter.hasNext()) {
1465 Object annotation= iter.next();
1466 if (annotation instanceof JavaProjectionAnnotation) {
1467 JavaProjectionAnnotation java= (JavaProjectionAnnotation) annotation;
1468
1469 java.generated_6809230897511527110(filter, expand, modified);
1470
1471 }
1472 }
1473
1474 model.modifyAnnotations(null, null, modified.toArray(new Annotation[modified.size()]));
1475 }
1476}