1 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 ; 32 import java.lang.ref.WeakReference ; 33 import java.util.ArrayList ; 34 import java.util.ConcurrentModificationException ; 35 import java.util.HashMap ; 36 import java.util.List ; 37 import java.util.Map ; 38 import java.util.TreeMap ; 39 import java.util.WeakHashMap ; 40 import javax.swing.SwingUtilities ; 41 import javax.swing.event.DocumentEvent ; 42 import javax.swing.text.BadLocationException ; 43 import javax.swing.text.Document ; 44 import javax.swing.text.JTextComponent ; 45 import javax.swing.text.Position ; 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 70 public class JavaElementFoldManager extends JavaFoldManager { 71 72 private FoldOperation operation; 73 private FileObject file; 74 private JavaElementFoldTask task; 75 76 private boolean foldImportsPreset; 78 private boolean foldInnerClassesPreset; 79 private boolean foldJavadocsPreset; 80 private boolean foldCodeBlocksPreset; 81 private boolean foldInitialCommentsPreset; 82 83 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 doc = operation.getHierarchy().getComponent().getDocument(); 95 DataObject od = (DataObject) doc.getProperty(Document.StreamDescriptionProperty); 96 97 if (od != null) { 98 currentFolds = new HashMap <FoldInfo, Fold>(); 99 task = JavaElementFoldTask.getTask(od.getPrimaryFile()); 100 task.setJavaElementFoldManager(JavaElementFoldManager.this); 101 } 102 } 103 104 public void insertUpdate(DocumentEvent evt, FoldHierarchyTransaction transaction) { 105 } 106 107 public void removeUpdate(DocumentEvent evt, FoldHierarchyTransaction transaction) { 108 } 109 110 public void changedUpdate(DocumentEvent 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; } 122 if (initialCommentFold == damagedFold) { 123 initialCommentFold = null; } 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 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 settingName){ 151 JTextComponent 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 private static Map <FileObject, JavaElementFoldTask> file2Task = new WeakHashMap (); 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 <JavaElementFoldManager> manager; 171 172 synchronized void setJavaElementFoldManager(JavaElementFoldManager manager) { 173 this.manager = new WeakReference (manager); 174 } 175 176 public void run(final CompilationInfo info) { 177 resume(); 178 179 JavaElementFoldManager manager; 180 181 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 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 { 218 219 private boolean insideRender; 220 private List <FoldInfo> infos; 221 private long startTime; 222 223 public CommitFolds(List <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 <FoldInfo, Fold> added = new TreeMap (); 246 List <FoldInfo> removed = new ArrayList <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 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 <FoldInfo, Fold> currentFolds; 309 private Fold initialCommentFold; 310 private Fold importsFold; 311 312 private final class JavaElementFoldVisitor extends CancellableTreePathScanner { 313 314 private List <FoldInfo> folds = new ArrayList (); 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 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 e) { 350 stopped = true; 352 } catch (ConcurrentModificationException e) { 353 stopped = true; 355 } 356 } 357 358 private void handleJavadoc(Tree t) throws BadLocationException , ConcurrentModificationException { 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; } 370 371 while (ts.movePrevious()) { 372 Token<JavaTokenId> token = ts.token(); 373 374 if (token.id() == JavaTokenId.JAVADOC_COMMENT) { 375 Document 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 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 e) { 399 stopped = true; 401 } catch (ConcurrentModificationException e) { 402 stopped = true; 404 } 405 } 406 407 @Override 408 public Object visitMethod(MethodTree node, Object p) { 409 super.visitMethod(node, p); 410 handleTree(node.getBody(), node, false); 411 return null; 412 } 413 414 @Override 415 public Object visitClass(ClassTree node, Object p) { 416 super.visitClass(node, Boolean.TRUE); 417 try { 418 if (p == Boolean.TRUE) { 419 Document 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 e) { 429 stopped = true; 431 } catch (ConcurrentModificationException e) { 432 stopped = true; 434 } 435 return null; 436 } 437 438 @Override 439 public Object visitVariable(VariableTree node,Object p) { 440 super.visitVariable(node, p); 441 handleTree(node, null, true); 442 return null; 443 } 444 445 @Override 446 public Object visitBlock(BlockTree node, Object p) { 447 super.visitBlock(node, p); 448 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 459 public Object visitCompilationUnit(CompilationUnitTree node, Object 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 doc = operation.getHierarchy().getComponent().getDocument(); 478 boolean collapsed = foldImportsPreset; 479 480 if (importsFold != null) { 481 collapsed = importsFold.isCollapsed(); 482 } 483 484 importsStart += 7; 485 486 if (importsStart < importsEnd) { 487 folds.add(new FoldInfo(doc, importsStart , importsEnd, IMPORTS_FOLD_TEMPLATE, collapsed)); 488 } 489 } catch (BadLocationException e) { 490 stopped = true; 492 } 493 } 494 return super.visitCompilationUnit(node, p); 495 } 496 497 } 498 499 protected static final class FoldInfo implements Comparable { 500 501 private Position start; 502 private Position end; 503 private FoldTemplate template; 504 private boolean collapseByDefault; 505 506 public FoldInfo(Document doc, int start, int end, FoldTemplate template, boolean collapseByDefault) throws BadLocationException { 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 o) { 518 if (!(o instanceof FoldInfo)) 519 return false; 520 521 return compareTo(o) == 0; 522 } 523 524 public int compareTo(Object 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 |