Source Code Cross Referenced for DefaultJavaFoldingStructureProvider.java in  » IDE-Eclipse » jdt » org » eclipse » jdt » ui » text » folding » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Eclipse » jdt » org.eclipse.jdt.ui.text.folding 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*******************************************************************************
0002:         * Copyright (c) 2006, 2007 IBM Corporation and others.
0003:         * All rights reserved. This program and the accompanying materials
0004:         * are made available under the terms of the Eclipse Public License v1.0
0005:         * which accompanies this distribution, and is available at
0006:         * http://www.eclipse.org/legal/epl-v10.html
0007:         *
0008:         * Contributors:
0009:         *     IBM Corporation - initial API and implementation
0010:         *******************************************************************************/package org.eclipse.jdt.ui.text.folding;
0011:
0012:        import java.util.ArrayList;
0013:        import java.util.Arrays;
0014:        import java.util.Collection;
0015:        import java.util.Collections;
0016:        import java.util.Comparator;
0017:        import java.util.HashMap;
0018:        import java.util.HashSet;
0019:        import java.util.Iterator;
0020:        import java.util.LinkedHashMap;
0021:        import java.util.List;
0022:        import java.util.Map;
0023:        import java.util.Set;
0024:
0025:        import org.eclipse.core.runtime.Assert;
0026:
0027:        import org.eclipse.jface.preference.IPreferenceStore;
0028:
0029:        import org.eclipse.jface.text.BadLocationException;
0030:        import org.eclipse.jface.text.IDocument;
0031:        import org.eclipse.jface.text.IRegion;
0032:        import org.eclipse.jface.text.Position;
0033:        import org.eclipse.jface.text.Region;
0034:        import org.eclipse.jface.text.TextSelection;
0035:        import org.eclipse.jface.text.source.Annotation;
0036:        import org.eclipse.jface.text.source.projection.IProjectionListener;
0037:        import org.eclipse.jface.text.source.projection.IProjectionPosition;
0038:        import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
0039:        import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
0040:        import org.eclipse.jface.text.source.projection.ProjectionViewer;
0041:
0042:        import org.eclipse.ui.texteditor.IDocumentProvider;
0043:        import org.eclipse.ui.texteditor.ITextEditor;
0044:
0045:        import org.eclipse.jdt.core.ElementChangedEvent;
0046:        import org.eclipse.jdt.core.IClassFile;
0047:        import org.eclipse.jdt.core.ICompilationUnit;
0048:        import org.eclipse.jdt.core.IElementChangedListener;
0049:        import org.eclipse.jdt.core.IImportContainer;
0050:        import org.eclipse.jdt.core.IImportDeclaration;
0051:        import org.eclipse.jdt.core.IJavaElement;
0052:        import org.eclipse.jdt.core.IJavaElementDelta;
0053:        import org.eclipse.jdt.core.IMember;
0054:        import org.eclipse.jdt.core.IParent;
0055:        import org.eclipse.jdt.core.ISourceRange;
0056:        import org.eclipse.jdt.core.ISourceReference;
0057:        import org.eclipse.jdt.core.IType;
0058:        import org.eclipse.jdt.core.JavaCore;
0059:        import org.eclipse.jdt.core.JavaModelException;
0060:        import org.eclipse.jdt.core.ToolFactory;
0061:        import org.eclipse.jdt.core.compiler.IProblem;
0062:        import org.eclipse.jdt.core.compiler.IScanner;
0063:        import org.eclipse.jdt.core.compiler.ITerminalSymbols;
0064:        import org.eclipse.jdt.core.compiler.InvalidInputException;
0065:        import org.eclipse.jdt.core.dom.CompilationUnit;
0066:
0067:        import org.eclipse.jdt.internal.corext.SourceRange;
0068:
0069:        import org.eclipse.jdt.ui.PreferenceConstants;
0070:
0071:        import org.eclipse.jdt.internal.ui.JavaPlugin;
0072:        import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
0073:        import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
0074:        import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
0075:        import org.eclipse.jdt.internal.ui.text.DocumentCharacterIterator;
0076:
0077:        /**
0078:         * Updates the projection model of a class file or compilation unit.
0079:         * <p>
0080:         * Clients may instantiate or subclass. Subclasses must make sure to always call the superclass'
0081:         * code when overriding methods that are marked with "subclasses may extend".
0082:         * </p>
0083:         * 
0084:         * @since 3.0 (internal)
0085:         * @since 3.2 (API)
0086:         */
0087:        public class DefaultJavaFoldingStructureProvider implements 
0088:                IJavaFoldingStructureProvider,
0089:                IJavaFoldingStructureProviderExtension {
0090:            /**
0091:             * A context that contains the information needed to compute the folding structure of an
0092:             * {@link ICompilationUnit} or an {@link IClassFile}. Computed folding regions are collected
0093:             * via
0094:             * {@linkplain #addProjectionRange(DefaultJavaFoldingStructureProvider.JavaProjectionAnnotation, Position) addProjectionRange}.
0095:             */
0096:            protected final class FoldingStructureComputationContext {
0097:                private final ProjectionAnnotationModel fModel;
0098:                private final IDocument fDocument;
0099:
0100:                private final boolean fAllowCollapsing;
0101:
0102:                private IType fFirstType;
0103:                private boolean fHasHeaderComment;
0104:                private LinkedHashMap fMap = new LinkedHashMap();
0105:                private IScanner fScanner;
0106:
0107:                private FoldingStructureComputationContext(IDocument document,
0108:                        ProjectionAnnotationModel model,
0109:                        boolean allowCollapsing, IScanner scanner) {
0110:                    Assert.isNotNull(document);
0111:                    Assert.isNotNull(model);
0112:                    fDocument = document;
0113:                    fModel = model;
0114:                    fAllowCollapsing = allowCollapsing;
0115:                    fScanner = scanner;
0116:                }
0117:
0118:                private void setFirstType(IType type) {
0119:                    if (hasFirstType())
0120:                        throw new IllegalStateException();
0121:                    fFirstType = type;
0122:                }
0123:
0124:                boolean hasFirstType() {
0125:                    return fFirstType != null;
0126:                }
0127:
0128:                private IType getFirstType() {
0129:                    return fFirstType;
0130:                }
0131:
0132:                private boolean hasHeaderComment() {
0133:                    return fHasHeaderComment;
0134:                }
0135:
0136:                private void setHasHeaderComment() {
0137:                    fHasHeaderComment = true;
0138:                }
0139:
0140:                /**
0141:                 * Returns <code>true</code> if newly created folding regions may be collapsed,
0142:                 * <code>false</code> if not. This is usually <code>false</code> when updating the
0143:                 * folding structure while typing; it may be <code>true</code> when computing or restoring
0144:                 * the initial folding structure.
0145:                 * 
0146:                 * @return <code>true</code> if newly created folding regions may be collapsed,
0147:                 *         <code>false</code> if not
0148:                 */
0149:                public boolean allowCollapsing() {
0150:                    return fAllowCollapsing;
0151:                }
0152:
0153:                /**
0154:                 * Returns the document which contains the code being folded.
0155:                 * 
0156:                 * @return the document which contains the code being folded
0157:                 */
0158:                private IDocument getDocument() {
0159:                    return fDocument;
0160:                }
0161:
0162:                private ProjectionAnnotationModel getModel() {
0163:                    return fModel;
0164:                }
0165:
0166:                private IScanner getScanner() {
0167:                    if (fScanner == null)
0168:                        fScanner = ToolFactory.createScanner(true, false,
0169:                                false, false);
0170:                    return fScanner;
0171:                }
0172:
0173:                /**
0174:                 * Adds a projection (folding) region to this context. The created annotation / position
0175:                 * pair will be added to the {@link ProjectionAnnotationModel} of the
0176:                 * {@link ProjectionViewer} of the editor.
0177:                 * 
0178:                 * @param annotation the annotation to add
0179:                 * @param position the corresponding position
0180:                 */
0181:                public void addProjectionRange(
0182:                        JavaProjectionAnnotation annotation, Position position) {
0183:                    fMap.put(annotation, position);
0184:                }
0185:
0186:                /**
0187:                 * Returns <code>true</code> if header comments should be collapsed.
0188:                 * 
0189:                 * @return <code>true</code> if header comments should be collapsed
0190:                 */
0191:                public boolean collapseHeaderComments() {
0192:                    return fAllowCollapsing && fCollapseHeaderComments;
0193:                }
0194:
0195:                /**
0196:                 * Returns <code>true</code> if import containers should be collapsed.
0197:                 * 
0198:                 * @return <code>true</code> if import containers should be collapsed
0199:                 */
0200:                public boolean collapseImportContainer() {
0201:                    return fAllowCollapsing && fCollapseImportContainer;
0202:                }
0203:
0204:                /**
0205:                 * Returns <code>true</code> if inner types should be collapsed.
0206:                 * 
0207:                 * @return <code>true</code> if inner types should be collapsed
0208:                 */
0209:                public boolean collapseInnerTypes() {
0210:                    return fAllowCollapsing && fCollapseInnerTypes;
0211:                }
0212:
0213:                /**
0214:                 * Returns <code>true</code> if javadoc comments should be collapsed.
0215:                 * 
0216:                 * @return <code>true</code> if javadoc comments should be collapsed
0217:                 */
0218:                public boolean collapseJavadoc() {
0219:                    return fAllowCollapsing && fCollapseJavadoc;
0220:                }
0221:
0222:                /**
0223:                 * Returns <code>true</code> if methods should be collapsed.
0224:                 * 
0225:                 * @return <code>true</code> if methods should be collapsed
0226:                 */
0227:                public boolean collapseMembers() {
0228:                    return fAllowCollapsing && fCollapseMembers;
0229:                }
0230:            }
0231:
0232:            /**
0233:             * A {@link ProjectionAnnotation} for java code.
0234:             */
0235:            protected static final class JavaProjectionAnnotation extends
0236:                    ProjectionAnnotation {
0237:
0238:                private IJavaElement fJavaElement;
0239:                private boolean fIsComment;
0240:
0241:                /**
0242:                 * Creates a new projection annotation.
0243:                 * 
0244:                 * @param isCollapsed <code>true</code> to set the initial state to collapsed,
0245:                 *        <code>false</code> to set it to expanded
0246:                 * @param element the java element this annotation refers to
0247:                 * @param isComment <code>true</code> for a foldable comment, <code>false</code> for a
0248:                 *        foldable code element
0249:                 */
0250:                public JavaProjectionAnnotation(boolean isCollapsed,
0251:                        IJavaElement element, boolean isComment) {
0252:                    super (isCollapsed);
0253:                    fJavaElement = element;
0254:                    fIsComment = isComment;
0255:                }
0256:
0257:                IJavaElement getElement() {
0258:                    return fJavaElement;
0259:                }
0260:
0261:                void setElement(IJavaElement element) {
0262:                    fJavaElement = element;
0263:                }
0264:
0265:                boolean isComment() {
0266:                    return fIsComment;
0267:                }
0268:
0269:                void setIsComment(boolean isComment) {
0270:                    fIsComment = isComment;
0271:                }
0272:
0273:                /*
0274:                 * @see java.lang.Object#toString()
0275:                 */
0276:                public String toString() {
0277:                    return "JavaProjectionAnnotation:\n" + //$NON-NLS-1$
0278:                            "\telement: \t" + fJavaElement.toString() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
0279:                            "\tcollapsed: \t" + isCollapsed() + "\n" + //$NON-NLS-1$ //$NON-NLS-2$
0280:                            "\tcomment: \t" + isComment() + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
0281:                }
0282:            }
0283:
0284:            private static final class Tuple {
0285:                JavaProjectionAnnotation annotation;
0286:                Position position;
0287:
0288:                Tuple(JavaProjectionAnnotation annotation, Position position) {
0289:                    this .annotation = annotation;
0290:                    this .position = position;
0291:                }
0292:            }
0293:
0294:            /**
0295:             * Filter for annotations.
0296:             */
0297:            private static interface Filter {
0298:                boolean match(JavaProjectionAnnotation annotation);
0299:            }
0300:
0301:            /**
0302:             * Matches comments.
0303:             */
0304:            private static final class CommentFilter implements  Filter {
0305:                public boolean match(JavaProjectionAnnotation annotation) {
0306:                    if (annotation.isComment() && !annotation.isMarkedDeleted()) {
0307:                        return true;
0308:                    }
0309:                    return false;
0310:                }
0311:            }
0312:
0313:            /**
0314:             * Matches members.
0315:             */
0316:            private static final class MemberFilter implements  Filter {
0317:                public boolean match(JavaProjectionAnnotation annotation) {
0318:                    if (!annotation.isComment()
0319:                            && !annotation.isMarkedDeleted()) {
0320:                        IJavaElement element = annotation.getElement();
0321:                        if (element instanceof  IMember) {
0322:                            if (element.getElementType() != IJavaElement.TYPE
0323:                                    || ((IMember) element).getDeclaringType() != null) {
0324:                                return true;
0325:                            }
0326:                        }
0327:                    }
0328:                    return false;
0329:                }
0330:            }
0331:
0332:            /**
0333:             * Matches java elements contained in a certain set.
0334:             */
0335:            private static final class JavaElementSetFilter implements  Filter {
0336:                private final Set/*<? extends IJavaElement>*/fSet;
0337:                private final boolean fMatchCollapsed;
0338:
0339:                private JavaElementSetFilter(
0340:                        Set/*<? extends IJavaElement>*/set,
0341:                        boolean matchCollapsed) {
0342:                    fSet = set;
0343:                    fMatchCollapsed = matchCollapsed;
0344:                }
0345:
0346:                public boolean match(JavaProjectionAnnotation annotation) {
0347:                    boolean stateMatch = fMatchCollapsed == annotation
0348:                            .isCollapsed();
0349:                    if (stateMatch && !annotation.isComment()
0350:                            && !annotation.isMarkedDeleted()) {
0351:                        IJavaElement element = annotation.getElement();
0352:                        if (fSet.contains(element)) {
0353:                            return true;
0354:                        }
0355:                    }
0356:                    return false;
0357:                }
0358:            }
0359:
0360:            private class ElementChangedListener implements 
0361:                    IElementChangedListener {
0362:
0363:                /*
0364:                 * @see org.eclipse.jdt.core.IElementChangedListener#elementChanged(org.eclipse.jdt.core.ElementChangedEvent)
0365:                 */
0366:                public void elementChanged(ElementChangedEvent e) {
0367:                    IJavaElementDelta delta = findElement(fInput, e.getDelta());
0368:                    if (delta != null
0369:                            && (delta.getFlags() & (IJavaElementDelta.F_CONTENT | IJavaElementDelta.F_CHILDREN)) != 0) {
0370:
0371:                        if (shouldIgnoreDelta(e.getDelta()
0372:                                .getCompilationUnitAST(), delta))
0373:                            return;
0374:
0375:                        fUpdatingCount++;
0376:                        try {
0377:                            update(createContext(false));
0378:                        } finally {
0379:                            fUpdatingCount--;
0380:                        }
0381:                    }
0382:                }
0383:
0384:                /**
0385:                 * Ignore the delta if there are errors on the caret line.
0386:                 * <p>
0387:                 * We don't ignore the delta if an import is added and the
0388:                 * caret isn't inside the import container.
0389:                 * </p> 
0390:                 *  
0391:                 * @param ast the compilation unit AST
0392:                 * @param delta the Java element delta for the given AST element
0393:                 * @return <code>true</code> if the delta should be ignored
0394:                 * @since 3.3
0395:                 */
0396:                private boolean shouldIgnoreDelta(CompilationUnit ast,
0397:                        IJavaElementDelta delta) {
0398:                    if (ast == null)
0399:                        return false; // can't compute
0400:
0401:                    IDocument document = getDocument();
0402:                    if (document == null)
0403:                        return false; // can't compute
0404:
0405:                    JavaEditor editor = fEditor;
0406:                    if (editor == null
0407:                            || editor.getCachedSelectedRange() == null)
0408:                        return false; // can't compute
0409:
0410:                    try {
0411:                        if (delta.getAffectedChildren().length == 1
0412:                                && delta.getAffectedChildren()[0].getElement() instanceof  IImportContainer) {
0413:                            IJavaElement elem = SelectionConverter
0414:                                    .getElementAtOffset(
0415:                                            ast.getJavaElement(),
0416:                                            new TextSelection(
0417:                                                    editor
0418:                                                            .getCachedSelectedRange().x,
0419:                                                    editor
0420:                                                            .getCachedSelectedRange().y));
0421:                            if (!(elem instanceof  IImportDeclaration))
0422:                                return false;
0423:
0424:                        }
0425:                    } catch (JavaModelException e) {
0426:                        return false; // can't compute
0427:                    }
0428:
0429:                    int caretLine = 0;
0430:                    try {
0431:                        caretLine = document.getLineOfOffset(editor
0432:                                .getCachedSelectedRange().x) + 1;
0433:                    } catch (BadLocationException x) {
0434:                        return false; // can't compute
0435:                    }
0436:
0437:                    if (caretLine > 0 && ast != null) {
0438:                        IProblem[] problems = ast.getProblems();
0439:                        for (int i = 0; i < problems.length; i++) {
0440:                            if (problems[i].isError()
0441:                                    && caretLine == problems[i]
0442:                                            .getSourceLineNumber())
0443:                                return true;
0444:                        }
0445:                    }
0446:
0447:                    return false;
0448:                }
0449:
0450:                private IJavaElementDelta findElement(IJavaElement target,
0451:                        IJavaElementDelta delta) {
0452:
0453:                    if (delta == null || target == null)
0454:                        return null;
0455:
0456:                    IJavaElement element = delta.getElement();
0457:
0458:                    if (element.getElementType() > IJavaElement.CLASS_FILE)
0459:                        return null;
0460:
0461:                    if (target.equals(element))
0462:                        return delta;
0463:
0464:                    IJavaElementDelta[] children = delta.getAffectedChildren();
0465:
0466:                    for (int i = 0; i < children.length; i++) {
0467:                        IJavaElementDelta d = findElement(target, children[i]);
0468:                        if (d != null)
0469:                            return d;
0470:                    }
0471:
0472:                    return null;
0473:                }
0474:            }
0475:
0476:            /**
0477:             * Projection position that will return two foldable regions: one folding away
0478:             * the region from after the '/**' to the beginning of the content, the other
0479:             * from after the first content line until after the comment.
0480:             */
0481:            private static final class CommentPosition extends Position
0482:                    implements  IProjectionPosition {
0483:                CommentPosition(int offset, int length) {
0484:                    super (offset, length);
0485:                }
0486:
0487:                /*
0488:                 * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeFoldingRegions(org.eclipse.jface.text.IDocument)
0489:                 */
0490:                public IRegion[] computeProjectionRegions(IDocument document)
0491:                        throws BadLocationException {
0492:                    DocumentCharacterIterator sequence = new DocumentCharacterIterator(
0493:                            document, offset, offset + length);
0494:                    int prefixEnd = 0;
0495:                    int contentStart = findFirstContent(sequence, prefixEnd);
0496:
0497:                    int firstLine = document
0498:                            .getLineOfOffset(offset + prefixEnd);
0499:                    int captionLine = document.getLineOfOffset(offset
0500:                            + contentStart);
0501:                    int lastLine = document.getLineOfOffset(offset + length);
0502:
0503:                    Assert
0504:                            .isTrue(firstLine <= captionLine,
0505:                                    "first folded line is greater than the caption line"); //$NON-NLS-1$
0506:                    Assert
0507:                            .isTrue(captionLine <= lastLine,
0508:                                    "caption line is greater than the last folded line"); //$NON-NLS-1$
0509:
0510:                    IRegion preRegion;
0511:                    if (firstLine < captionLine) {
0512:                        //				preRegion= new Region(offset + prefixEnd, contentStart - prefixEnd);
0513:                        int preOffset = document.getLineOffset(firstLine);
0514:                        IRegion preEndLineInfo = document
0515:                                .getLineInformation(captionLine);
0516:                        int preEnd = preEndLineInfo.getOffset();
0517:                        preRegion = new Region(preOffset, preEnd - preOffset);
0518:                    } else {
0519:                        preRegion = null;
0520:                    }
0521:
0522:                    if (captionLine < lastLine) {
0523:                        int postOffset = document
0524:                                .getLineOffset(captionLine + 1);
0525:                        IRegion postRegion = new Region(postOffset, offset
0526:                                + length - postOffset);
0527:
0528:                        if (preRegion == null)
0529:                            return new IRegion[] { postRegion };
0530:
0531:                        return new IRegion[] { preRegion, postRegion };
0532:                    }
0533:
0534:                    if (preRegion != null)
0535:                        return new IRegion[] { preRegion };
0536:
0537:                    return null;
0538:                }
0539:
0540:                /**
0541:                 * Finds the offset of the first identifier part within <code>content</code>.
0542:                 * Returns 0 if none is found.
0543:                 *
0544:                 * @param content the content to search
0545:                 * @param prefixEnd the end of the prefix
0546:                 * @return the first index of a unicode identifier part, or zero if none can
0547:                 *         be found
0548:                 */
0549:                private int findFirstContent(final CharSequence content,
0550:                        int prefixEnd) {
0551:                    int lenght = content.length();
0552:                    for (int i = prefixEnd; i < lenght; i++) {
0553:                        if (Character
0554:                                .isUnicodeIdentifierPart(content.charAt(i)))
0555:                            return i;
0556:                    }
0557:                    return 0;
0558:                }
0559:
0560:                //		/**
0561:                //		 * Finds the offset of the first identifier part within <code>content</code>.
0562:                //		 * Returns 0 if none is found.
0563:                //		 *
0564:                //		 * @param content the content to search
0565:                //		 * @return the first index of a unicode identifier part, or zero if none can
0566:                //		 *         be found
0567:                //		 */
0568:                //		private int findPrefixEnd(final CharSequence content) {
0569:                //			// return the index after the leading '/*' or '/**'
0570:                //			int len= content.length();
0571:                //			int i= 0;
0572:                //			while (i < len && isWhiteSpace(content.charAt(i)))
0573:                //				i++;
0574:                //			if (len >= i + 2 && content.charAt(i) == '/' && content.charAt(i + 1) == '*')
0575:                //				if (len >= i + 3 && content.charAt(i + 2) == '*')
0576:                //					return i + 3;
0577:                //				else
0578:                //					return i + 2;
0579:                //			else
0580:                //				return i;
0581:                //		}
0582:                //
0583:                //		private boolean isWhiteSpace(char c) {
0584:                //			return c == ' ' || c == '\t';
0585:                //		}
0586:
0587:                /*
0588:                 * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeCaptionOffset(org.eclipse.jface.text.IDocument)
0589:                 */
0590:                public int computeCaptionOffset(IDocument document) {
0591:                    //			return 0;
0592:                    DocumentCharacterIterator sequence = new DocumentCharacterIterator(
0593:                            document, offset, offset + length);
0594:                    return findFirstContent(sequence, 0);
0595:                }
0596:            }
0597:
0598:            /**
0599:             * Projection position that will return two foldable regions: one folding away
0600:             * the lines before the one containing the simple name of the java element, one
0601:             * folding away any lines after the caption.
0602:             */
0603:            private static final class JavaElementPosition extends Position
0604:                    implements  IProjectionPosition {
0605:
0606:                private IMember fMember;
0607:
0608:                public JavaElementPosition(int offset, int length,
0609:                        IMember member) {
0610:                    super (offset, length);
0611:                    Assert.isNotNull(member);
0612:                    fMember = member;
0613:                }
0614:
0615:                public void setMember(IMember member) {
0616:                    Assert.isNotNull(member);
0617:                    fMember = member;
0618:                }
0619:
0620:                /*
0621:                 * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeFoldingRegions(org.eclipse.jface.text.IDocument)
0622:                 */
0623:                public IRegion[] computeProjectionRegions(IDocument document)
0624:                        throws BadLocationException {
0625:                    int nameStart = offset;
0626:                    try {
0627:                        /* The member's name range may not be correct. However,
0628:                         * reconciling would trigger another element delta which would
0629:                         * lead to reentrant situations. Therefore, we optimistically
0630:                         * assume that the name range is correct, but double check the
0631:                         * received lines below. */
0632:                        ISourceRange nameRange = fMember.getNameRange();
0633:                        if (nameRange != null)
0634:                            nameStart = nameRange.getOffset();
0635:
0636:                    } catch (JavaModelException e) {
0637:                        // ignore and use default
0638:                    }
0639:
0640:                    int firstLine = document.getLineOfOffset(offset);
0641:                    int captionLine = document.getLineOfOffset(nameStart);
0642:                    int lastLine = document.getLineOfOffset(offset + length);
0643:
0644:                    /* see comment above - adjust the caption line to be inside the
0645:                     * entire folded region, and rely on later element deltas to correct
0646:                     * the name range. */
0647:                    if (captionLine < firstLine)
0648:                        captionLine = firstLine;
0649:                    if (captionLine > lastLine)
0650:                        captionLine = lastLine;
0651:
0652:                    IRegion preRegion;
0653:                    if (firstLine < captionLine) {
0654:                        int preOffset = document.getLineOffset(firstLine);
0655:                        IRegion preEndLineInfo = document
0656:                                .getLineInformation(captionLine);
0657:                        int preEnd = preEndLineInfo.getOffset();
0658:                        preRegion = new Region(preOffset, preEnd - preOffset);
0659:                    } else {
0660:                        preRegion = null;
0661:                    }
0662:
0663:                    if (captionLine < lastLine) {
0664:                        int postOffset = document
0665:                                .getLineOffset(captionLine + 1);
0666:                        IRegion postRegion = new Region(postOffset, offset
0667:                                + length - postOffset);
0668:
0669:                        if (preRegion == null)
0670:                            return new IRegion[] { postRegion };
0671:
0672:                        return new IRegion[] { preRegion, postRegion };
0673:                    }
0674:
0675:                    if (preRegion != null)
0676:                        return new IRegion[] { preRegion };
0677:
0678:                    return null;
0679:                }
0680:
0681:                /*
0682:                 * @see org.eclipse.jface.text.source.projection.IProjectionPosition#computeCaptionOffset(org.eclipse.jface.text.IDocument)
0683:                 */
0684:                public int computeCaptionOffset(IDocument document)
0685:                        throws BadLocationException {
0686:                    int nameStart = offset;
0687:                    try {
0688:                        // need a reconcile here?
0689:                        ISourceRange nameRange = fMember.getNameRange();
0690:                        if (nameRange != null)
0691:                            nameStart = nameRange.getOffset();
0692:                    } catch (JavaModelException e) {
0693:                        // ignore and use default
0694:                    }
0695:
0696:                    return nameStart - offset;
0697:                }
0698:
0699:            }
0700:
0701:            /**
0702:             * Internal projection listener.
0703:             */
0704:            private final class ProjectionListener implements 
0705:                    IProjectionListener {
0706:                private ProjectionViewer fViewer;
0707:
0708:                /**
0709:                 * Registers the listener with the viewer.
0710:                 * 
0711:                 * @param viewer the viewer to register a listener with
0712:                 */
0713:                public ProjectionListener(ProjectionViewer viewer) {
0714:                    Assert.isLegal(viewer != null);
0715:                    fViewer = viewer;
0716:                    fViewer.addProjectionListener(this );
0717:                }
0718:
0719:                /**
0720:                 * Disposes of this listener and removes the projection listener from the viewer.
0721:                 */
0722:                public void dispose() {
0723:                    if (fViewer != null) {
0724:                        fViewer.removeProjectionListener(this );
0725:                        fViewer = null;
0726:                    }
0727:                }
0728:
0729:                /*
0730:                 * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionEnabled()
0731:                 */
0732:                public void projectionEnabled() {
0733:                    handleProjectionEnabled();
0734:                }
0735:
0736:                /*
0737:                 * @see org.eclipse.jface.text.source.projection.IProjectionListener#projectionDisabled()
0738:                 */
0739:                public void projectionDisabled() {
0740:                    handleProjectionDisabled();
0741:                }
0742:            }
0743:
0744:            /* context and listeners */
0745:            private JavaEditor fEditor;
0746:            private ProjectionListener fProjectionListener;
0747:            private IJavaElement fInput;
0748:            private IElementChangedListener fElementListener;
0749:
0750:            /* preferences */
0751:            private boolean fCollapseJavadoc = false;
0752:            private boolean fCollapseImportContainer = true;
0753:            private boolean fCollapseInnerTypes = true;
0754:            private boolean fCollapseMembers = false;
0755:            private boolean fCollapseHeaderComments = true;
0756:
0757:            /* filters */
0758:            /** Member filter, matches nested members (but not top-level types). */
0759:            private final Filter fMemberFilter = new MemberFilter();
0760:            /** Comment filter, matches comments. */
0761:            private final Filter fCommentFilter = new CommentFilter();
0762:
0763:            /**
0764:             * Reusable scanner.
0765:             * @since 3.3
0766:             */
0767:            private IScanner fSharedScanner = ToolFactory.createScanner(true,
0768:                    false, false, false);
0769:
0770:            private volatile int fUpdatingCount = 0;
0771:
0772:            /**
0773:             * Creates a new folding provider. It must be
0774:             * {@link #install(ITextEditor, ProjectionViewer) installed} on an editor/viewer pair before it
0775:             * can be used, and {@link #uninstall() uninstalled} when not used any longer.
0776:             * <p>
0777:             * The projection state may be reset by calling {@link #initialize()}.
0778:             * </p>
0779:             */
0780:            public DefaultJavaFoldingStructureProvider() {
0781:            }
0782:
0783:            /**
0784:             * {@inheritDoc}
0785:             * <p>
0786:             * Subclasses may extend.
0787:             * </p>
0788:             * 
0789:             * @param editor {@inheritDoc}
0790:             * @param viewer {@inheritDoc}
0791:             */
0792:            public void install(ITextEditor editor, ProjectionViewer viewer) {
0793:                Assert.isLegal(editor != null);
0794:                Assert.isLegal(viewer != null);
0795:
0796:                internalUninstall();
0797:
0798:                if (editor instanceof  JavaEditor) {
0799:                    fProjectionListener = new ProjectionListener(viewer);
0800:                    fEditor = (JavaEditor) editor;
0801:                }
0802:            }
0803:
0804:            /**
0805:             * {@inheritDoc}
0806:             * <p>
0807:             * Subclasses may extend.
0808:             * </p>
0809:             */
0810:            public void uninstall() {
0811:                internalUninstall();
0812:            }
0813:
0814:            /**
0815:             * Internal implementation of {@link #uninstall()}.
0816:             */
0817:            private void internalUninstall() {
0818:                if (isInstalled()) {
0819:                    handleProjectionDisabled();
0820:                    fProjectionListener.dispose();
0821:                    fProjectionListener = null;
0822:                    fEditor = null;
0823:                }
0824:            }
0825:
0826:            /**
0827:             * Returns <code>true</code> if the provider is installed, <code>false</code> otherwise.
0828:             * 
0829:             * @return <code>true</code> if the provider is installed, <code>false</code> otherwise
0830:             */
0831:            protected final boolean isInstalled() {
0832:                return fEditor != null;
0833:            }
0834:
0835:            /**
0836:             * Called whenever projection is enabled, for example when the viewer issues a
0837:             * {@link IProjectionListener#projectionEnabled() projectionEnabled} message. When the provider
0838:             * is already enabled when this method is called, it is first
0839:             * {@link #handleProjectionDisabled() disabled}.
0840:             * <p>
0841:             * Subclasses may extend.
0842:             * </p>
0843:             */
0844:            protected void handleProjectionEnabled() {
0845:                // http://home.ott.oti.com/teams/wswb/anon/out/vms/index.html
0846:                // projectionEnabled messages are not always paired with projectionDisabled
0847:                // i.e. multiple enabled messages may be sent out.
0848:                // we have to make sure that we disable first when getting an enable
0849:                // message.
0850:                handleProjectionDisabled();
0851:
0852:                if (isInstalled()) {
0853:                    initialize();
0854:                    fElementListener = new ElementChangedListener();
0855:                    JavaCore.addElementChangedListener(fElementListener);
0856:                }
0857:            }
0858:
0859:            /**
0860:             * Called whenever projection is disabled, for example when the provider is
0861:             * {@link #uninstall() uninstalled}, when the viewer issues a
0862:             * {@link IProjectionListener#projectionDisabled() projectionDisabled} message and before
0863:             * {@link #handleProjectionEnabled() enabling} the provider. Implementations must be prepared to
0864:             * handle multiple calls to this method even if the provider is already disabled.
0865:             * <p>
0866:             * Subclasses may extend.
0867:             * </p>
0868:             */
0869:            protected void handleProjectionDisabled() {
0870:                if (fElementListener != null) {
0871:                    JavaCore.removeElementChangedListener(fElementListener);
0872:                    fElementListener = null;
0873:                }
0874:            }
0875:
0876:            /*
0877:             * @see org.eclipse.jdt.ui.text.folding.IJavaFoldingStructureProvider#initialize()
0878:             */
0879:            public final void initialize() {
0880:                fUpdatingCount++;
0881:                try {
0882:                    update(createInitialContext());
0883:                } finally {
0884:                    fUpdatingCount--;
0885:                }
0886:            }
0887:
0888:            private FoldingStructureComputationContext createInitialContext() {
0889:                initializePreferences();
0890:                fInput = getInputElement();
0891:                if (fInput == null)
0892:                    return null;
0893:
0894:                return createContext(true);
0895:            }
0896:
0897:            private FoldingStructureComputationContext createContext(
0898:                    boolean allowCollapse) {
0899:                if (!isInstalled())
0900:                    return null;
0901:                ProjectionAnnotationModel model = getModel();
0902:                if (model == null)
0903:                    return null;
0904:                IDocument doc = getDocument();
0905:                if (doc == null)
0906:                    return null;
0907:
0908:                IScanner scanner = null;
0909:                if (fUpdatingCount == 1)
0910:                    scanner = fSharedScanner; // reuse scanner
0911:
0912:                return new FoldingStructureComputationContext(doc, model,
0913:                        allowCollapse, scanner);
0914:            }
0915:
0916:            private IJavaElement getInputElement() {
0917:                if (fEditor == null)
0918:                    return null;
0919:                return EditorUtility.getEditorInputJavaElement(fEditor, false);
0920:            }
0921:
0922:            private void initializePreferences() {
0923:                IPreferenceStore store = JavaPlugin.getDefault()
0924:                        .getPreferenceStore();
0925:                fCollapseInnerTypes = store
0926:                        .getBoolean(PreferenceConstants.EDITOR_FOLDING_INNERTYPES);
0927:                fCollapseImportContainer = store
0928:                        .getBoolean(PreferenceConstants.EDITOR_FOLDING_IMPORTS);
0929:                fCollapseJavadoc = store
0930:                        .getBoolean(PreferenceConstants.EDITOR_FOLDING_JAVADOC);
0931:                fCollapseMembers = store
0932:                        .getBoolean(PreferenceConstants.EDITOR_FOLDING_METHODS);
0933:                fCollapseHeaderComments = store
0934:                        .getBoolean(PreferenceConstants.EDITOR_FOLDING_HEADERS);
0935:            }
0936:
0937:            private void update(FoldingStructureComputationContext ctx) {
0938:                if (ctx == null)
0939:                    return;
0940:
0941:                Map additions = new HashMap();
0942:                List deletions = new ArrayList();
0943:                List updates = new ArrayList();
0944:
0945:                computeFoldingStructure(ctx);
0946:                Map newStructure = ctx.fMap;
0947:                Map oldStructure = computeCurrentStructure(ctx);
0948:
0949:                Iterator e = newStructure.keySet().iterator();
0950:                while (e.hasNext()) {
0951:                    JavaProjectionAnnotation newAnnotation = (JavaProjectionAnnotation) e
0952:                            .next();
0953:                    Position newPosition = (Position) newStructure
0954:                            .get(newAnnotation);
0955:
0956:                    IJavaElement element = newAnnotation.getElement();
0957:                    /*
0958:                     * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=130472 and
0959:                     * https://bugs.eclipse.org/bugs/show_bug.cgi?id=127445 In the presence of syntax
0960:                     * errors, anonymous types may have a source range offset of 0. When such a situation is
0961:                     * encountered, we ignore the proposed folding range: if no corresponding folding range
0962:                     * exists, it is silently ignored; if there *is* a matching folding range, we ignore the
0963:                     * position update and keep the old range, in order to keep the folding structure
0964:                     * stable.
0965:                     */
0966:                    boolean isMalformedAnonymousType = newPosition.getOffset() == 0
0967:                            && element.getElementType() == IJavaElement.TYPE
0968:                            && isInnerType((IType) element);
0969:                    List annotations = (List) oldStructure.get(element);
0970:                    if (annotations == null) {
0971:                        if (!isMalformedAnonymousType)
0972:                            additions.put(newAnnotation, newPosition);
0973:                    } else {
0974:                        Iterator x = annotations.iterator();
0975:                        boolean matched = false;
0976:                        while (x.hasNext()) {
0977:                            Tuple tuple = (Tuple) x.next();
0978:                            JavaProjectionAnnotation existingAnnotation = tuple.annotation;
0979:                            Position existingPosition = tuple.position;
0980:                            if (newAnnotation.isComment() == existingAnnotation
0981:                                    .isComment()) {
0982:                                boolean updateCollapsedState = ctx
0983:                                        .allowCollapsing()
0984:                                        && existingAnnotation.isCollapsed() != newAnnotation
0985:                                                .isCollapsed();
0986:                                if (!isMalformedAnonymousType
0987:                                        && existingPosition != null
0988:                                        && (!newPosition
0989:                                                .equals(existingPosition) || updateCollapsedState)) {
0990:                                    existingPosition.setOffset(newPosition
0991:                                            .getOffset());
0992:                                    existingPosition.setLength(newPosition
0993:                                            .getLength());
0994:                                    if (updateCollapsedState)
0995:                                        if (newAnnotation.isCollapsed())
0996:                                            existingAnnotation.markCollapsed();
0997:                                        else
0998:                                            existingAnnotation.markExpanded();
0999:                                    updates.add(existingAnnotation);
1000:                                }
1001:                                matched = true;
1002:                                x.remove();
1003:                                break;
1004:                            }
1005:                        }
1006:                        if (!matched)
1007:                            additions.put(newAnnotation, newPosition);
1008:
1009:                        if (annotations.isEmpty())
1010:                            oldStructure.remove(element);
1011:                    }
1012:                }
1013:
1014:                e = oldStructure.values().iterator();
1015:                while (e.hasNext()) {
1016:                    List list = (List) e.next();
1017:                    int size = list.size();
1018:                    for (int i = 0; i < size; i++)
1019:                        deletions.add(((Tuple) list.get(i)).annotation);
1020:                }
1021:
1022:                match(deletions, additions, updates, ctx);
1023:
1024:                Annotation[] deletedArray = (Annotation[]) deletions
1025:                        .toArray(new Annotation[deletions.size()]);
1026:                Annotation[] changedArray = (Annotation[]) updates
1027:                        .toArray(new Annotation[updates.size()]);
1028:                ctx.getModel().modifyAnnotations(deletedArray, additions,
1029:                        changedArray);
1030:
1031:                ctx.fScanner.setSource(null);
1032:            }
1033:
1034:            private void computeFoldingStructure(
1035:                    FoldingStructureComputationContext ctx) {
1036:                IParent parent = (IParent) fInput;
1037:                try {
1038:                    if (!(fInput instanceof  ISourceReference))
1039:                        return;
1040:                    String source = ((ISourceReference) fInput).getSource();
1041:                    if (source == null)
1042:                        return;
1043:
1044:                    ctx.getScanner().setSource(source.toCharArray());
1045:                    computeFoldingStructure(parent.getChildren(), ctx);
1046:                } catch (JavaModelException x) {
1047:                }
1048:            }
1049:
1050:            private void computeFoldingStructure(IJavaElement[] elements,
1051:                    FoldingStructureComputationContext ctx)
1052:                    throws JavaModelException {
1053:                for (int i = 0; i < elements.length; i++) {
1054:                    IJavaElement element = elements[i];
1055:
1056:                    computeFoldingStructure(element, ctx);
1057:
1058:                    if (element instanceof  IParent) {
1059:                        IParent parent = (IParent) element;
1060:                        computeFoldingStructure(parent.getChildren(), ctx);
1061:                    }
1062:                }
1063:            }
1064:
1065:            /**
1066:             * Computes the folding structure for a given {@link IJavaElement java element}. Computed
1067:             * projection annotations are
1068:             * {@link DefaultJavaFoldingStructureProvider.FoldingStructureComputationContext#addProjectionRange(DefaultJavaFoldingStructureProvider.JavaProjectionAnnotation, Position) added}
1069:             * to the computation context.
1070:             * <p>
1071:             * Subclasses may extend or replace. The default implementation creates projection annotations
1072:             * for the following elements:
1073:             * <ul>
1074:             * <li>true members (not for top-level types)</li>
1075:             * <li>the javadoc comments of any member</li>
1076:             * <li>header comments (javadoc or multi-line comments appearing before the first type's
1077:             * javadoc or before the package or import declarations).</li>
1078:             * </ul>
1079:             * </p>
1080:             * 
1081:             * @param element the java element to compute the folding structure for
1082:             * @param ctx the computation context
1083:             */
1084:            protected void computeFoldingStructure(IJavaElement element,
1085:                    FoldingStructureComputationContext ctx) {
1086:
1087:                boolean collapse = false;
1088:                boolean collapseCode = true;
1089:                switch (element.getElementType()) {
1090:
1091:                case IJavaElement.IMPORT_CONTAINER:
1092:                    collapse = ctx.collapseImportContainer();
1093:                    break;
1094:                case IJavaElement.TYPE:
1095:                    collapseCode = isInnerType((IType) element)
1096:                            && !isAnonymousEnum((IType) element);
1097:                    collapse = ctx.collapseInnerTypes() && collapseCode;
1098:                    break;
1099:                case IJavaElement.METHOD:
1100:                case IJavaElement.FIELD:
1101:                case IJavaElement.INITIALIZER:
1102:                    collapse = ctx.collapseMembers();
1103:                    break;
1104:                default:
1105:                    return;
1106:                }
1107:
1108:                IRegion[] regions = computeProjectionRanges(
1109:                        (ISourceReference) element, ctx);
1110:                if (regions.length > 0) {
1111:                    // comments
1112:                    for (int i = 0; i < regions.length - 1; i++) {
1113:                        IRegion normalized = alignRegion(regions[i], ctx);
1114:                        if (normalized != null) {
1115:                            Position position = createCommentPosition(normalized);
1116:                            if (position != null) {
1117:                                boolean commentCollapse;
1118:                                if (i == 0
1119:                                        && (regions.length > 2 || ctx
1120:                                                .hasHeaderComment())
1121:                                        && element == ctx.getFirstType()) {
1122:                                    commentCollapse = ctx
1123:                                            .collapseHeaderComments();
1124:                                } else {
1125:                                    commentCollapse = ctx.collapseJavadoc();
1126:                                }
1127:                                ctx
1128:                                        .addProjectionRange(
1129:                                                new JavaProjectionAnnotation(
1130:                                                        commentCollapse,
1131:                                                        element, true),
1132:                                                position);
1133:                            }
1134:                        }
1135:                    }
1136:                    // code
1137:                    if (collapseCode) {
1138:                        IRegion normalized = alignRegion(
1139:                                regions[regions.length - 1], ctx);
1140:                        if (normalized != null) {
1141:                            Position position = element instanceof  IMember ? createMemberPosition(
1142:                                    normalized, (IMember) element)
1143:                                    : createCommentPosition(normalized);
1144:                            if (position != null)
1145:                                ctx.addProjectionRange(
1146:                                        new JavaProjectionAnnotation(collapse,
1147:                                                element, false), position);
1148:                        }
1149:                    }
1150:                }
1151:            }
1152:
1153:            /**
1154:             * Returns <code>true</code> if <code>type</code> is an anonymous enum declaration,
1155:             * <code>false</code> otherwise. See also https://bugs.eclipse.org/bugs/show_bug.cgi?id=143276
1156:             * 
1157:             * @param type the type to test
1158:             * @return <code>true</code> if <code>type</code> is an anonymous enum declaration
1159:             * @since 3.3
1160:             */
1161:            private boolean isAnonymousEnum(IType type) {
1162:                try {
1163:                    return type.isEnum() && type.isAnonymous();
1164:                } catch (JavaModelException x) {
1165:                    return false; // optimistically
1166:                }
1167:            }
1168:
1169:            /**
1170:             * Returns <code>true</code> if <code>type</code> is not a top-level type, <code>false</code> if it is.
1171:             * 
1172:             * @param type the type to test
1173:             * @return <code>true</code> if <code>type</code> is an inner type
1174:             */
1175:            private boolean isInnerType(IType type) {
1176:                return type.getDeclaringType() != null;
1177:            }
1178:
1179:            /**
1180:             * Computes the projection ranges for a given <code>ISourceReference</code>. More than one
1181:             * range or none at all may be returned. If there are no foldable regions, an empty array is
1182:             * returned.
1183:             * <p>
1184:             * The last region in the returned array (if not empty) describes the region for the java
1185:             * element that implements the source reference. Any preceding regions describe javadoc comments
1186:             * of that java element.
1187:             * </p>
1188:             * 
1189:             * @param reference a java element that is a source reference
1190:             * @param ctx the folding context
1191:             * @return the regions to be folded
1192:             */
1193:            protected final IRegion[] computeProjectionRanges(
1194:                    ISourceReference reference,
1195:                    FoldingStructureComputationContext ctx) {
1196:                try {
1197:                    ISourceRange range = reference.getSourceRange();
1198:                    if (!SourceRange.isAvailable(range))
1199:                        return new IRegion[0];
1200:
1201:                    String contents = reference.getSource();
1202:                    if (contents == null)
1203:                        return new IRegion[0];
1204:
1205:                    List regions = new ArrayList();
1206:                    if (!ctx.hasFirstType() && reference instanceof  IType) {
1207:                        ctx.setFirstType((IType) reference);
1208:                        IRegion headerComment = computeHeaderComment(ctx);
1209:                        if (headerComment != null) {
1210:                            regions.add(headerComment);
1211:                            ctx.setHasHeaderComment();
1212:                        }
1213:                    }
1214:
1215:                    final int shift = range.getOffset();
1216:                    IScanner scanner = ctx.getScanner();
1217:                    scanner.resetTo(shift, shift + range.getLength());
1218:
1219:                    int start = shift;
1220:                    while (true) {
1221:
1222:                        int token = scanner.getNextToken();
1223:                        start = scanner.getCurrentTokenStartPosition();
1224:
1225:                        switch (token) {
1226:                        case ITerminalSymbols.TokenNameCOMMENT_JAVADOC:
1227:                        case ITerminalSymbols.TokenNameCOMMENT_BLOCK: {
1228:                            int end = scanner.getCurrentTokenEndPosition() + 1;
1229:                            regions.add(new Region(start, end - start));
1230:                            continue;
1231:                        }
1232:                        case ITerminalSymbols.TokenNameCOMMENT_LINE:
1233:                            continue;
1234:                        }
1235:
1236:                        break;
1237:                    }
1238:
1239:                    regions.add(new Region(start, shift + range.getLength()
1240:                            - start));
1241:
1242:                    IRegion[] result = new IRegion[regions.size()];
1243:                    regions.toArray(result);
1244:                    return result;
1245:                } catch (JavaModelException e) {
1246:                } catch (InvalidInputException e) {
1247:                }
1248:
1249:                return new IRegion[0];
1250:            }
1251:
1252:            private IRegion computeHeaderComment(
1253:                    FoldingStructureComputationContext ctx)
1254:                    throws JavaModelException {
1255:                // search at most up to the first type
1256:                ISourceRange range = ctx.getFirstType().getSourceRange();
1257:                if (range == null)
1258:                    return null;
1259:                int start = 0;
1260:                int end = range.getOffset();
1261:
1262:                /* code adapted from CommentFormattingStrategy:
1263:                 * scan the header content up to the first type. Once a comment is
1264:                 * found, accumulate any additional comments up to the stop condition.
1265:                 * The stop condition is reaching a package declaration, import container,
1266:                 * or the end of the input.
1267:                 */
1268:                IScanner scanner = ctx.getScanner();
1269:                scanner.resetTo(start, end);
1270:
1271:                int headerStart = -1;
1272:                int headerEnd = -1;
1273:                try {
1274:                    boolean foundComment = false;
1275:                    int terminal = scanner.getNextToken();
1276:                    while (terminal != ITerminalSymbols.TokenNameEOF
1277:                            && !(terminal == ITerminalSymbols.TokenNameclass
1278:                                    || terminal == ITerminalSymbols.TokenNameinterface
1279:                                    || terminal == ITerminalSymbols.TokenNameenum || (foundComment && (terminal == ITerminalSymbols.TokenNameimport || terminal == ITerminalSymbols.TokenNamepackage)))) {
1280:
1281:                        if (terminal == ITerminalSymbols.TokenNameCOMMENT_JAVADOC
1282:                                || terminal == ITerminalSymbols.TokenNameCOMMENT_BLOCK
1283:                                || terminal == ITerminalSymbols.TokenNameCOMMENT_LINE) {
1284:                            if (!foundComment)
1285:                                headerStart = scanner
1286:                                        .getCurrentTokenStartPosition();
1287:                            headerEnd = scanner.getCurrentTokenEndPosition();
1288:                            foundComment = true;
1289:                        }
1290:                        terminal = scanner.getNextToken();
1291:                    }
1292:
1293:                } catch (InvalidInputException ex) {
1294:                    return null;
1295:                }
1296:
1297:                if (headerEnd != -1) {
1298:                    return new Region(headerStart, headerEnd - headerStart);
1299:                }
1300:                return null;
1301:            }
1302:
1303:            /**
1304:             * Creates a comment folding position from an
1305:             * {@link #alignRegion(IRegion, DefaultJavaFoldingStructureProvider.FoldingStructureComputationContext) aligned}
1306:             * region.
1307:             * 
1308:             * @param aligned an aligned region
1309:             * @return a folding position corresponding to <code>aligned</code>
1310:             */
1311:            protected final Position createCommentPosition(IRegion aligned) {
1312:                return new CommentPosition(aligned.getOffset(), aligned
1313:                        .getLength());
1314:            }
1315:
1316:            /**
1317:             * Creates a folding position that remembers its member from an
1318:             * {@link #alignRegion(IRegion, DefaultJavaFoldingStructureProvider.FoldingStructureComputationContext) aligned}
1319:             * region.
1320:             * 
1321:             * @param aligned an aligned region
1322:             * @param member the member to remember
1323:             * @return a folding position corresponding to <code>aligned</code>
1324:             */
1325:            protected final Position createMemberPosition(IRegion aligned,
1326:                    IMember member) {
1327:                return new JavaElementPosition(aligned.getOffset(), aligned
1328:                        .getLength(), member);
1329:            }
1330:
1331:            /**
1332:             * Aligns <code>region</code> to start and end at a line offset. The region's start is
1333:             * decreased to the next line offset, and the end offset increased to the next line start or the
1334:             * end of the document. <code>null</code> is returned if <code>region</code> is
1335:             * <code>null</code> itself or does not comprise at least one line delimiter, as a single line
1336:             * cannot be folded.
1337:             * 
1338:             * @param region the region to align, may be <code>null</code>
1339:             * @param ctx the folding context
1340:             * @return a region equal or greater than <code>region</code> that is aligned with line
1341:             *         offsets, <code>null</code> if the region is too small to be foldable (e.g. covers
1342:             *         only one line)
1343:             */
1344:            protected final IRegion alignRegion(IRegion region,
1345:                    FoldingStructureComputationContext ctx) {
1346:                if (region == null)
1347:                    return null;
1348:
1349:                IDocument document = ctx.getDocument();
1350:
1351:                try {
1352:
1353:                    int start = document.getLineOfOffset(region.getOffset());
1354:                    int end = document.getLineOfOffset(region.getOffset()
1355:                            + region.getLength());
1356:                    if (start >= end)
1357:                        return null;
1358:
1359:                    int offset = document.getLineOffset(start);
1360:                    int endOffset;
1361:                    if (document.getNumberOfLines() > end + 1)
1362:                        endOffset = document.getLineOffset(end + 1);
1363:                    else
1364:                        endOffset = document.getLineOffset(end)
1365:                                + document.getLineLength(end);
1366:
1367:                    return new Region(offset, endOffset - offset);
1368:
1369:                } catch (BadLocationException x) {
1370:                    // concurrent modification
1371:                    return null;
1372:                }
1373:            }
1374:
1375:            private ProjectionAnnotationModel getModel() {
1376:                return (ProjectionAnnotationModel) fEditor
1377:                        .getAdapter(ProjectionAnnotationModel.class);
1378:            }
1379:
1380:            private IDocument getDocument() {
1381:                JavaEditor editor = fEditor;
1382:                if (editor == null)
1383:                    return null;
1384:
1385:                IDocumentProvider provider = editor.getDocumentProvider();
1386:                if (provider == null)
1387:                    return null;
1388:
1389:                return provider.getDocument(editor.getEditorInput());
1390:            }
1391:
1392:            /**
1393:             * Matches deleted annotations to changed or added ones. A deleted
1394:             * annotation/position tuple that has a matching addition / change
1395:             * is updated and marked as changed. The matching tuple is not added
1396:             * (for additions) or marked as deletion instead (for changes). The
1397:             * result is that more annotations are changed and fewer get
1398:             * deleted/re-added.
1399:             * 
1400:             * @param deletions list with deleted annotations
1401:             * @param additions map with position to annotation mappings
1402:             * @param changes list with changed annotations
1403:             * @param ctx	the context 
1404:             */
1405:            private void match(List deletions, Map additions, List changes,
1406:                    FoldingStructureComputationContext ctx) {
1407:                if (deletions.isEmpty()
1408:                        || (additions.isEmpty() && changes.isEmpty()))
1409:                    return;
1410:
1411:                List newDeletions = new ArrayList();
1412:                List newChanges = new ArrayList();
1413:
1414:                Iterator deletionIterator = deletions.iterator();
1415:                while (deletionIterator.hasNext()) {
1416:                    JavaProjectionAnnotation deleted = (JavaProjectionAnnotation) deletionIterator
1417:                            .next();
1418:                    Position deletedPosition = ctx.getModel().getPosition(
1419:                            deleted);
1420:                    if (deletedPosition == null)
1421:                        continue;
1422:
1423:                    Tuple deletedTuple = new Tuple(deleted, deletedPosition);
1424:
1425:                    Tuple match = findMatch(deletedTuple, changes, null, ctx);
1426:                    boolean addToDeletions = true;
1427:                    if (match == null) {
1428:                        match = findMatch(deletedTuple, additions.keySet(),
1429:                                additions, ctx);
1430:                        addToDeletions = false;
1431:                    }
1432:
1433:                    if (match != null) {
1434:                        IJavaElement element = match.annotation.getElement();
1435:                        deleted.setElement(element);
1436:                        deletedPosition.setLength(match.position.getLength());
1437:                        if (deletedPosition instanceof  JavaElementPosition
1438:                                && element instanceof  IMember) {
1439:                            JavaElementPosition jep = (JavaElementPosition) deletedPosition;
1440:                            jep.setMember((IMember) element);
1441:                        }
1442:
1443:                        deletionIterator.remove();
1444:                        newChanges.add(deleted);
1445:
1446:                        if (addToDeletions)
1447:                            newDeletions.add(match.annotation);
1448:                    }
1449:                }
1450:
1451:                deletions.addAll(newDeletions);
1452:                changes.addAll(newChanges);
1453:            }
1454:
1455:            /**
1456:             * Finds a match for <code>tuple</code> in a collection of
1457:             * annotations. The positions for the
1458:             * <code>JavaProjectionAnnotation</code> instances in
1459:             * <code>annotations</code> can be found in the passed
1460:             * <code>positionMap</code> or <code>fCachedModel</code> if
1461:             * <code>positionMap</code> is <code>null</code>.
1462:             * <p>
1463:             * A tuple is said to match another if their annotations have the
1464:             * same comment flag and their position offsets are equal.
1465:             * </p>
1466:             * <p>
1467:             * If a match is found, the annotation gets removed from
1468:             * <code>annotations</code>.
1469:             * </p>
1470:             * 
1471:             * @param tuple the tuple for which we want to find a match
1472:             * @param annotations collection of
1473:             *        <code>JavaProjectionAnnotation</code>
1474:             * @param positionMap a <code>Map&lt;Annotation, Position&gt;</code>
1475:             *        or <code>null</code>
1476:             * @param ctx the context
1477:             * @return a matching tuple or <code>null</code> for no match
1478:             */
1479:            private Tuple findMatch(Tuple tuple, Collection annotations,
1480:                    Map positionMap, FoldingStructureComputationContext ctx) {
1481:                Iterator it = annotations.iterator();
1482:                while (it.hasNext()) {
1483:                    JavaProjectionAnnotation annotation = (JavaProjectionAnnotation) it
1484:                            .next();
1485:                    if (tuple.annotation.isComment() == annotation.isComment()) {
1486:                        Position position = positionMap == null ? ctx
1487:                                .getModel().getPosition(annotation)
1488:                                : (Position) positionMap.get(annotation);
1489:                        if (position == null)
1490:                            continue;
1491:
1492:                        if (tuple.position.getOffset() == position.getOffset()) {
1493:                            it.remove();
1494:                            return new Tuple(annotation, position);
1495:                        }
1496:                    }
1497:                }
1498:
1499:                return null;
1500:            }
1501:
1502:            private Map computeCurrentStructure(
1503:                    FoldingStructureComputationContext ctx) {
1504:                Map map = new HashMap();
1505:                ProjectionAnnotationModel model = ctx.getModel();
1506:                Iterator e = model.getAnnotationIterator();
1507:                while (e.hasNext()) {
1508:                    Object annotation = e.next();
1509:                    if (annotation instanceof  JavaProjectionAnnotation) {
1510:                        JavaProjectionAnnotation java = (JavaProjectionAnnotation) annotation;
1511:                        Position position = model.getPosition(java);
1512:                        Assert.isNotNull(position);
1513:                        List list = (List) map.get(java.getElement());
1514:                        if (list == null) {
1515:                            list = new ArrayList(2);
1516:                            map.put(java.getElement(), list);
1517:                        }
1518:                        list.add(new Tuple(java, position));
1519:                    }
1520:                }
1521:
1522:                Comparator comparator = new Comparator() {
1523:                    public int compare(Object o1, Object o2) {
1524:                        return ((Tuple) o1).position.getOffset()
1525:                                - ((Tuple) o2).position.getOffset();
1526:                    }
1527:                };
1528:                for (Iterator it = map.values().iterator(); it.hasNext();) {
1529:                    List list = (List) it.next();
1530:                    Collections.sort(list, comparator);
1531:                }
1532:                return map;
1533:            }
1534:
1535:            /*
1536:             * @see IJavaFoldingStructureProviderExtension#collapseMembers()
1537:             * @since 3.2
1538:             */
1539:            public final void collapseMembers() {
1540:                modifyFiltered(fMemberFilter, false);
1541:            }
1542:
1543:            /*
1544:             * @see IJavaFoldingStructureProviderExtension#collapseComments()
1545:             * @since 3.2
1546:             */
1547:            public final void collapseComments() {
1548:                modifyFiltered(fCommentFilter, false);
1549:            }
1550:
1551:            /*
1552:             * @see org.eclipse.jdt.ui.text.folding.IJavaFoldingStructureProviderExtension#collapseElements(org.eclipse.jdt.core.IJavaElement[])
1553:             */
1554:            public final void collapseElements(IJavaElement[] elements) {
1555:                Set set = new HashSet(Arrays.asList(elements));
1556:                modifyFiltered(new JavaElementSetFilter(set, false), false);
1557:            }
1558:
1559:            /*
1560:             * @see org.eclipse.jdt.ui.text.folding.IJavaFoldingStructureProviderExtension#expandElements(org.eclipse.jdt.core.IJavaElement[])
1561:             */
1562:            public final void expandElements(IJavaElement[] elements) {
1563:                Set set = new HashSet(Arrays.asList(elements));
1564:                modifyFiltered(new JavaElementSetFilter(set, true), true);
1565:            }
1566:
1567:            /**
1568:             * Collapses or expands all annotations matched by the passed filter.
1569:             * 
1570:             * @param filter the filter to use to select which annotations to collapse
1571:             * @param expand <code>true</code> to expand the matched annotations, <code>false</code> to
1572:             *        collapse them
1573:             */
1574:            private void modifyFiltered(Filter filter, boolean expand) {
1575:                if (!isInstalled())
1576:                    return;
1577:
1578:                ProjectionAnnotationModel model = getModel();
1579:                if (model == null)
1580:                    return;
1581:
1582:                List modified = new ArrayList();
1583:                Iterator iter = model.getAnnotationIterator();
1584:                while (iter.hasNext()) {
1585:                    Object annotation = iter.next();
1586:                    if (annotation instanceof  JavaProjectionAnnotation) {
1587:                        JavaProjectionAnnotation java = (JavaProjectionAnnotation) annotation;
1588:
1589:                        if (expand == java.isCollapsed() && filter.match(java)) {
1590:                            if (expand)
1591:                                java.markExpanded();
1592:                            else
1593:                                java.markCollapsed();
1594:                            modified.add(java);
1595:                        }
1596:
1597:                    }
1598:                }
1599:
1600:                model.modifyAnnotations(null, null, (Annotation[]) modified
1601:                        .toArray(new Annotation[modified.size()]));
1602:            }
1603:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.