KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > editor > fold > JavaElementFoldManager


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.modules.java.editor.fold;
20
21 import com.sun.source.tree.BlockTree;
22 import com.sun.source.tree.ClassTree;
23 import com.sun.source.tree.CompilationUnitTree;
24 import com.sun.source.tree.ImportTree;
25 import com.sun.source.tree.MethodTree;
26 import com.sun.source.tree.Tree;
27 import com.sun.source.tree.Tree.Kind;
28 import com.sun.source.tree.VariableTree;
29 import com.sun.source.util.SourcePositions;
30 import com.sun.source.util.TreePath;
31 import java.lang.ref.Reference JavaDoc;
32 import java.lang.ref.WeakReference JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.ConcurrentModificationException JavaDoc;
35 import java.util.HashMap JavaDoc;
36 import java.util.List JavaDoc;
37 import java.util.Map JavaDoc;
38 import java.util.TreeMap JavaDoc;
39 import java.util.WeakHashMap JavaDoc;
40 import javax.swing.SwingUtilities JavaDoc;
41 import javax.swing.event.DocumentEvent JavaDoc;
42 import javax.swing.text.BadLocationException JavaDoc;
43 import javax.swing.text.Document JavaDoc;
44 import javax.swing.text.JTextComponent JavaDoc;
45 import javax.swing.text.Position JavaDoc;
46 import org.netbeans.api.editor.fold.Fold;
47 import org.netbeans.api.java.lexer.JavaTokenId;
48 import org.netbeans.api.java.source.CompilationInfo;
49 import org.netbeans.api.java.source.support.CancellableTreePathScanner;
50 import org.netbeans.api.lexer.Token;
51 import org.netbeans.api.lexer.TokenHierarchy;
52 import org.netbeans.api.lexer.TokenSequence;
53 import org.netbeans.api.timers.TimesCollector;
54 import org.netbeans.editor.SettingsChangeEvent;
55 import org.netbeans.editor.SettingsUtil;
56 import org.netbeans.editor.ext.java.JavaFoldManager;
57 import org.netbeans.editor.ext.java.JavaSettingsNames;
58 import org.netbeans.modules.java.editor.semantic.ScanningCancellableTask;
59 import org.netbeans.modules.java.editor.semantic.Utilities;
60 import org.netbeans.spi.editor.fold.FoldHierarchyTransaction;
61 import org.netbeans.spi.editor.fold.FoldOperation;
62 import org.openide.ErrorManager;
63 import org.openide.filesystems.FileObject;
64 import org.openide.loaders.DataObject;
65
66 /**
67  *
68  * @author Jan Lahoda
69  */

70 public class JavaElementFoldManager extends JavaFoldManager {
71     
72     private FoldOperation operation;
73     private FileObject file;
74     private JavaElementFoldTask task;
75     
76     // Folding presets
77
private boolean foldImportsPreset;
78     private boolean foldInnerClassesPreset;
79     private boolean foldJavadocsPreset;
80     private boolean foldCodeBlocksPreset;
81     private boolean foldInitialCommentsPreset;
82     
83     /** Creates a new instance of JavaElementFoldManager */
84     public JavaElementFoldManager() {
85     }
86
87     public void init(FoldOperation operation) {
88         this.operation = operation;
89         
90         settingsChange(null);
91     }
92
93     public synchronized void initFolds(FoldHierarchyTransaction transaction) {
94         Document JavaDoc doc = operation.getHierarchy().getComponent().getDocument();
95         DataObject od = (DataObject) doc.getProperty(Document.StreamDescriptionProperty);
96         
97         if (od != null) {
98             currentFolds = new HashMap JavaDoc<FoldInfo, Fold>();
99             task = JavaElementFoldTask.getTask(od.getPrimaryFile());
100             task.setJavaElementFoldManager(JavaElementFoldManager.this);
101         }
102     }
103     
104     public void insertUpdate(DocumentEvent JavaDoc evt, FoldHierarchyTransaction transaction) {
105     }
106
107     public void removeUpdate(DocumentEvent JavaDoc evt, FoldHierarchyTransaction transaction) {
108     }
109
110     public void changedUpdate(DocumentEvent JavaDoc evt, FoldHierarchyTransaction transaction) {
111     }
112
113     public void removeEmptyNotify(Fold emptyFold) {
114         removeDamagedNotify(emptyFold);
115     }
116
117     public void removeDamagedNotify(Fold damagedFold) {
118         currentFolds.remove(operation.getExtraInfo(damagedFold));
119         if (importsFold == damagedFold) {
120             importsFold = null;//not sure if this is correct...
121
}
122         if (initialCommentFold == damagedFold) {
123             initialCommentFold = null;//not sure if this is correct...
124
}
125     }
126
127     public void expandNotify(Fold expandedFold) {
128     }
129
130     public synchronized void release() {
131         if (task != null)
132             task.setJavaElementFoldManager(null);
133         
134         task = null;
135         file = null;
136         currentFolds = null;
137         importsFold = null;
138         initialCommentFold = null;
139     }
140     
141     public void settingsChange(SettingsChangeEvent evt) {
142         // Get folding presets
143
foldInitialCommentsPreset = getSetting(JavaSettingsNames.CODE_FOLDING_COLLAPSE_INITIAL_COMMENT);
144         foldImportsPreset = getSetting(JavaSettingsNames.CODE_FOLDING_COLLAPSE_IMPORT);
145         foldCodeBlocksPreset = getSetting(JavaSettingsNames.CODE_FOLDING_COLLAPSE_METHOD);
146         foldInnerClassesPreset = getSetting(JavaSettingsNames.CODE_FOLDING_COLLAPSE_INNERCLASS);
147         foldJavadocsPreset = getSetting(JavaSettingsNames.CODE_FOLDING_COLLAPSE_JAVADOC);
148     }
149     
150     private boolean getSetting(String JavaDoc settingName){
151         JTextComponent JavaDoc tc = operation.getHierarchy().getComponent();
152         return SettingsUtil.getBoolean(org.netbeans.editor.Utilities.getKitClass(tc), settingName, false);
153     }
154     
155     static final class JavaElementFoldTask extends ScanningCancellableTask<CompilationInfo> {
156         
157         //XXX: this will hold JavaElementFoldTask as long as the FileObject exists:
158
private static Map JavaDoc<FileObject, JavaElementFoldTask> file2Task = new WeakHashMap JavaDoc();
159         
160         static JavaElementFoldTask getTask(FileObject file) {
161             JavaElementFoldTask task = file2Task.get(file);
162             
163             if (task == null) {
164                 file2Task.put(file, task = new JavaElementFoldTask());
165             }
166             
167             return task;
168         }
169         
170         private Reference JavaDoc<JavaElementFoldManager> manager;
171         
172         synchronized void setJavaElementFoldManager(JavaElementFoldManager manager) {
173             this.manager = new WeakReference JavaDoc(manager);
174         }
175         
176         public void run(final CompilationInfo info) {
177             resume();
178             
179             JavaElementFoldManager manager;
180             
181             //the synchronized section should be as limited as possible here
182
//in particular, "scan" should not be called in the synchronized section
183
//or a deadlock could appear: sy(this)+document read lock against
184
//document write lock and this.cancel/sy(this)
185
synchronized (this) {
186                 manager = this.manager != null ? this.manager.get() : null;
187             }
188             
189             if (manager == null)
190                 return ;
191             
192             long startTime = System.currentTimeMillis();
193
194             final CompilationUnitTree cu = info.getCompilationUnit();
195             final JavaElementFoldVisitor v = manager.new JavaElementFoldVisitor(info, cu, info.getTrees().getSourcePositions());
196             
197             scan(v, cu, null);
198             
199             if (v.stopped || isCancelled())
200                 return ;
201             
202             //check for initial fold:
203
v.checkInitialFold();
204             
205             if (v.stopped || isCancelled())
206                 return ;
207             
208             SwingUtilities.invokeLater(manager.new CommitFolds(v.folds));
209             
210             long endTime = System.currentTimeMillis();
211             
212             TimesCollector.getDefault().reportTime(info.getFileObject(), "java-folds-1", "Folds - 1", endTime - startTime);
213         }
214         
215     }
216     
217     private class CommitFolds implements Runnable JavaDoc {
218         
219         private boolean insideRender;
220         private List JavaDoc<FoldInfo> infos;
221         private long startTime;
222         
223         public CommitFolds(List JavaDoc<FoldInfo> infos) {
224             this.infos = infos;
225         }
226         
227         public void run() {
228             if (!insideRender) {
229                 startTime = System.currentTimeMillis();
230                 insideRender = true;
231                 operation.getHierarchy().getComponent().getDocument().render(this);
232                 
233                 return;
234             }
235             
236             operation.getHierarchy().lock();
237             
238             try {
239                 FoldHierarchyTransaction tr = operation.openTransaction();
240                 
241                 try {
242                     if (currentFolds == null)
243                         return ;
244                     
245                     Map JavaDoc<FoldInfo, Fold> added = new TreeMap JavaDoc();
246                     List JavaDoc<FoldInfo> removed = new ArrayList JavaDoc<FoldInfo>(currentFolds.keySet());
247                     
248                     for (FoldInfo i : infos) {
249                         if (removed.remove(i)) {
250                             continue ;
251                         }
252                         
253                         int start = i.start.getOffset();
254                         int end = i.end.getOffset();
255                         
256                         if (end > start && (end - start) > (i.template.getStartGuardedLength() + i.template.getEndGuardedLength())) {
257                             Fold f = operation.addToHierarchy(i.template.getType(),
258                                                                  i.template.getDescription(),
259                                                                  i.collapseByDefault,
260                                                                  start,
261                                                                  end,
262                                                                  i.template.getStartGuardedLength(),
263                                                                  i.template.getEndGuardedLength(),
264                                                                  i,
265                                                                  tr);
266                             
267                             added.put(i, f);
268                             
269                             if (i.template == IMPORTS_FOLD_TEMPLATE) {
270                                 importsFold = f;
271                             }
272                             if (i.template == INITIAL_COMMENT_FOLD_TEMPLATE) {
273                                 initialCommentFold = f;
274                             }
275                         }
276                     }
277                     
278                     for (FoldInfo i : removed) {
279                         Fold f = currentFolds.remove(i);
280                         
281                         operation.removeFromHierarchy(f, tr);
282                         
283                         if (importsFold == f ) {
284                             importsFold = null;
285                         }
286                         
287                         if (initialCommentFold == f) {
288                             initialCommentFold = f;
289                         }
290                     }
291                     
292                     currentFolds.putAll(added);
293                 } catch (BadLocationException JavaDoc e) {
294                     ErrorManager.getDefault().notify(e);
295                 } finally {
296                     tr.commit();
297                 }
298             } finally {
299                 operation.getHierarchy().unlock();
300             }
301             
302             long endTime = System.currentTimeMillis();
303             
304             TimesCollector.getDefault().reportTime(file, "java-folds-2", "Folds - 2", endTime - startTime);
305         }
306     }
307     
308     private Map JavaDoc<FoldInfo, Fold> currentFolds;
309     private Fold initialCommentFold;
310     private Fold importsFold;
311     
312     private final class JavaElementFoldVisitor extends CancellableTreePathScanner {
313         
314         private List JavaDoc<FoldInfo> folds = new ArrayList JavaDoc();
315         private CompilationInfo info;
316         private CompilationUnitTree cu;
317         private SourcePositions sp;
318         private boolean stopped;
319         
320         public JavaElementFoldVisitor(CompilationInfo info, CompilationUnitTree cu, SourcePositions sp) {
321             this.info = info;
322             this.cu = cu;
323             this.sp = sp;
324         }
325         
326         public void checkInitialFold() {
327             try {
328                 TokenHierarchy th = info.getTokenHierarchy();
329                 TokenSequence<JavaTokenId> ts = th.tokenSequence(JavaTokenId.language());
330                 
331                 while (ts.moveNext()) {
332                     Token<JavaTokenId> token = ts.token();
333                     
334                     if (token.id() == JavaTokenId.BLOCK_COMMENT) {
335                         Document JavaDoc doc = operation.getHierarchy().getComponent().getDocument();
336                         int startOffset = ts.offset();
337                         boolean collapsed = foldInitialCommentsPreset;
338                         
339                         if (initialCommentFold != null) {
340                             collapsed = initialCommentFold.isCollapsed();
341                         }
342                         
343                         folds.add(new FoldInfo(doc, startOffset, startOffset + token.length(), INITIAL_COMMENT_FOLD_TEMPLATE, collapsed));
344                     }
345                     
346                     if (token.id() != JavaTokenId.WHITESPACE)
347                         break;
348                 }
349             } catch (BadLocationException JavaDoc e) {
350                 //the document probably changed, stop
351
stopped = true;
352             } catch (ConcurrentModificationException JavaDoc e) {
353                 //from TokenSequence, document probably changed, stop
354
stopped = true;
355             }
356         }
357         
358         private void handleJavadoc(Tree t) throws BadLocationException JavaDoc, ConcurrentModificationException JavaDoc {
359             int start = (int) sp.getStartPosition(cu, t);
360             
361             if (start == (-1))
362                 return ;
363             
364             TokenHierarchy th = info.getTokenHierarchy();
365             TokenSequence<JavaTokenId> ts = th.tokenSequence(JavaTokenId.language());
366             
367             if (ts.move(start) == Integer.MAX_VALUE) {
368                 return;//nothing
369
}
370             
371             while (ts.movePrevious()) {
372                 Token<JavaTokenId> token = ts.token();
373                 
374                 if (token.id() == JavaTokenId.JAVADOC_COMMENT) {
375                     Document JavaDoc doc = operation.getHierarchy().getComponent().getDocument();
376                     int startOffset = ts.offset();
377                     folds.add(new FoldInfo(doc, startOffset, startOffset + token.length(), JAVADOC_FOLD_TEMPLATE, foldJavadocsPreset));
378                 }
379                 if ( token.id() != JavaTokenId.WHITESPACE
380                     && token.id() != JavaTokenId.BLOCK_COMMENT
381                     && token.id() != JavaTokenId.LINE_COMMENT)
382                     break;
383             }
384         }
385         
386         private void handleTree(Tree node, Tree javadocTree, boolean handleOnlyJavadoc) {
387             try {
388                 if (!handleOnlyJavadoc) {
389                     Document JavaDoc doc = operation.getHierarchy().getComponent().getDocument();
390                     int start = (int)sp.getStartPosition(cu, node);
391                     int end = (int)sp.getEndPosition(cu, node);
392                     
393                     if (start != (-1) && end != (-1))
394                         folds.add(new FoldInfo(doc, start, end, CODE_BLOCK_FOLD_TEMPLATE, foldCodeBlocksPreset));
395                 }
396                 
397                 handleJavadoc(javadocTree != null ? javadocTree : node);
398             } catch (BadLocationException JavaDoc e) {
399                 //the document probably changed, stop
400
stopped = true;
401             } catch (ConcurrentModificationException JavaDoc e) {
402                 //from TokenSequence, document probably changed, stop
403
stopped = true;
404             }
405         }
406         
407         @Override JavaDoc
408         public Object JavaDoc visitMethod(MethodTree node, Object JavaDoc p) {
409             super.visitMethod(node, p);
410             handleTree(node.getBody(), node, false);
411             return null;
412         }
413
414         @Override JavaDoc
415         public Object JavaDoc visitClass(ClassTree node, Object JavaDoc p) {
416             super.visitClass(node, Boolean.TRUE);
417             try {
418                 if (p == Boolean.TRUE) {
419                     Document JavaDoc doc = operation.getHierarchy().getComponent().getDocument();
420                     int start = Utilities.findBodyStart(node, cu, sp, doc);
421                     int end = (int)sp.getEndPosition(cu, node);
422                     
423                     if (start != (-1) && end != (-1))
424                         folds.add(new FoldInfo(doc, start, end, CODE_BLOCK_FOLD_TEMPLATE, foldInnerClassesPreset));
425                 }
426                 
427                 handleJavadoc(node);
428             } catch (BadLocationException JavaDoc e) {
429                 //the document probably changed, stop
430
stopped = true;
431             } catch (ConcurrentModificationException JavaDoc e) {
432                 //from TokenSequence, document probably changed, stop
433
stopped = true;
434             }
435             return null;
436         }
437         
438         @Override JavaDoc
439         public Object JavaDoc visitVariable(VariableTree node,Object JavaDoc p) {
440             super.visitVariable(node, p);
441             handleTree(node, null, true);
442             return null;
443         }
444         
445         @Override JavaDoc
446         public Object JavaDoc visitBlock(BlockTree node, Object JavaDoc p) {
447             super.visitBlock(node, p);
448             //check static/dynamic initializer:
449
TreePath path = getCurrentPath();
450             
451             if (path.getParentPath().getLeaf().getKind() == Kind.CLASS) {
452                 handleTree(node, null, false);
453             }
454             
455             return null;
456         }
457         
458         @Override JavaDoc
459         public Object JavaDoc visitCompilationUnit(CompilationUnitTree node, Object JavaDoc p) {
460             int importsStart = Integer.MAX_VALUE;
461             int importsEnd = -1;
462             
463             for (ImportTree imp : node.getImports()) {
464                 int start = (int) sp.getStartPosition(cu, imp);
465                 int end = (int) sp.getEndPosition(cu, imp);
466                 
467                 if (importsStart > start)
468                     importsStart = start;
469                 
470                 if (end > importsEnd) {
471                     importsEnd = end;
472                 }
473             }
474             
475             if (importsEnd != (-1) && importsStart != (-1)) {
476                 try {
477                     Document JavaDoc doc = operation.getHierarchy().getComponent().getDocument();
478                     boolean collapsed = foldImportsPreset;
479                     
480                     if (importsFold != null) {
481                         collapsed = importsFold.isCollapsed();
482                     }
483                     
484                     importsStart += 7/*"import ".length()*/;
485                     
486                     if (importsStart < importsEnd) {
487                         folds.add(new FoldInfo(doc, importsStart , importsEnd, IMPORTS_FOLD_TEMPLATE, collapsed));
488                     }
489                 } catch (BadLocationException JavaDoc e) {
490                     //the document probably changed, stop
491
stopped = true;
492                 }
493             }
494             return super.visitCompilationUnit(node, p);
495         }
496
497     }
498     
499     protected static final class FoldInfo implements Comparable JavaDoc {
500         
501         private Position JavaDoc start;
502         private Position JavaDoc end;
503         private FoldTemplate template;
504         private boolean collapseByDefault;
505         
506         public FoldInfo(Document JavaDoc doc, int start, int end, FoldTemplate template, boolean collapseByDefault) throws BadLocationException JavaDoc {
507             this.start = doc.createPosition(start);
508             this.end = doc.createPosition(end);
509             this.template = template;
510             this.collapseByDefault = collapseByDefault;
511         }
512         
513         public int hashCode() {
514             return 1;
515         }
516         
517         public boolean equals(Object JavaDoc o) {
518             if (!(o instanceof FoldInfo))
519                 return false;
520             
521             return compareTo(o) == 0;
522         }
523         
524         public int compareTo(Object JavaDoc o) {
525             FoldInfo remote = (FoldInfo) o;
526             
527             if (start.getOffset() < remote.start.getOffset()) {
528                 return -1;
529             }
530             
531             if (start.getOffset() > remote.start.getOffset()) {
532                 return 1;
533             }
534             
535             if (end.getOffset() < remote.end.getOffset()) {
536                 return -1;
537             }
538             
539             if (end.getOffset() > remote.end.getOffset()) {
540                 return 1;
541             }
542             
543             return 0;
544         }
545         
546     }
547     
548 }
549
Popular Tags