1 19 20 package org.netbeans.modules.javacore.internalapi; 21 22 import java.beans.PropertyChangeListener ; 23 import java.beans.PropertyChangeSupport ; 24 import java.util.*; 25 import javax.swing.event.*; 26 import javax.swing.text.Document ; 27 import javax.swing.text.StyledDocument ; 28 import org.netbeans.api.java.classpath.*; 29 import org.netbeans.jmi.javamodel.JavaModelPackage; 30 import org.netbeans.jmi.javamodel.Resource; 31 import org.netbeans.modules.javacore.*; 32 import org.netbeans.modules.javacore.jmiimpl.javamodel.ResourceImpl; 33 import org.openide.LifecycleManager; 34 import org.openide.cookies.EditorCookie; 35 import org.openide.filesystems.*; 36 import org.openide.loaders.DataObject; 37 import org.openide.loaders.DataObjectNotFoundException; 38 import org.openide.text.CloneableEditorSupport; 39 import org.openide.text.NbDocument; 40 41 45 public class UndoManager extends FileChangeAdapter implements DocumentListener, ChangeListener , GlobalPathRegistryListener { 46 47 48 private LinkedList undoList; 49 50 51 private LinkedList redoList; 52 53 54 private final HashSet allCES = new HashSet(); 55 56 57 private final HashMap documentToCES = new HashMap(); 58 59 60 private final HashMap listenerToCES = new HashMap(); 61 private boolean listenersRegistered = false; 62 63 public static final String PROP_STATE = "state"; 65 private final PropertyChangeSupport pcs = new PropertyChangeSupport (this); 66 67 private boolean wasUndo = false; 68 private boolean wasRedo = false; 69 private boolean transactionStart; 70 private boolean dontDeleteUndo = false; 71 72 private IdentityHashMap descriptionMap; 73 private String description; 74 private HashSet modifiedResources; 75 private JMManager manager; 76 private ProgressListener progress; 77 78 79 80 public UndoManager() { 81 undoList = new LinkedList(); 82 redoList = new LinkedList(); 83 descriptionMap = new IdentityHashMap(); 84 modifiedResources = new HashSet(); 85 manager = (JMManager) JMManager.getManager(); 86 } 87 88 public UndoManager(ProgressListener progress) { 89 this(); 90 this.progress = progress; 91 } 92 93 public void setUndoDescription(String desc) { 94 description = desc; 95 } 96 97 public String getUndoDescription() { 98 if (undoList.isEmpty()) return null; 99 return (String ) descriptionMap.get(undoList.getFirst()); 100 } 101 102 public String getRedoDescription() { 103 if (redoList.isEmpty()) return null; 104 return (String ) descriptionMap.get(redoList.getFirst()); 105 } 106 107 109 public void transactionStarted() { 110 modifiedResources.clear(); 111 transactionStart = true; 112 unregisterListeners(); 113 RepositoryUpdater.getDefault().setListenOnChanges(false); 114 } 115 116 119 public void transactionEnded(boolean fail) { 120 try { 121 description = null; 122 parseModified(); 123 dontDeleteUndo = true; 124 if (fail && !undoList.isEmpty()) 125 undoList.removeFirst(); 126 else { 127 if (isUndoAvailable() && getUndoDescription() == null) { 129 descriptionMap.remove(undoList.removeFirst()); 130 dontDeleteUndo = false; 131 } 132 133 } 134 135 invalidate(null); 136 dontDeleteUndo = false; 137 } finally { 138 RepositoryUpdater.getDefault().setListenOnChanges(true); 139 registerListeners(); 140 } 141 fireStateChange(); 142 } 143 144 145 public void undo() { 146 if (isUndoAvailable()) { 148 JavaMetamodel.getDefaultRepository().beginTrans(true); 149 boolean fail = true; 150 try { 151 JMManager.getTransactionMutex().disableModifications(); 152 transactionStarted(); 153 wasUndo = true; 154 LinkedList undo = (LinkedList) undoList.getFirst(); 155 fireProgressListenerStart(0, undo.size()); 156 undoList.removeFirst(); 157 Iterator undoIterator = undo.iterator(); 158 UndoItem item; 159 redoList.addFirst(new LinkedList()); 160 descriptionMap.put(redoList.getFirst(), descriptionMap.remove(undo)); 161 while (undoIterator.hasNext()) { 162 fireProgressListenerStep(); 163 item = (UndoItem) undoIterator.next(); 164 item.undo(); 165 if (item instanceof ExternalUndoItem) { 166 addItem(item); 167 } else { 168 modifiedResources.add(manager.getFileObject(((UndoManager.ResourceUndoItem)item).getResource())); 170 } 171 } 172 fail = false; 173 } catch (RuntimeException e) { 174 e.printStackTrace(); 175 } finally { 176 try { 177 wasUndo = false; 178 JavaMetamodel.getDefaultRepository().endTrans(fail); 179 transactionEnded(fail); 180 } finally { 181 fireProgressListenerStop(); 182 fireStateChange(); 183 } 184 } 185 } 186 } 187 188 190 public void redo() { 191 if (isRedoAvailable()) { 193 JavaMetamodel.getDefaultRepository().beginTrans(true); 194 boolean fail = true; 195 try { 196 JMManager.getTransactionMutex().disableModifications(); 197 transactionStarted(); 198 wasRedo = true; 199 LinkedList redo = (LinkedList) redoList.getFirst(); 200 fireProgressListenerStart(1, redo.size()); 201 redoList.removeFirst(); 202 Iterator redoIterator = redo.iterator(); 203 UndoItem item; 204 description = (String ) descriptionMap.remove(redo); 205 while (redoIterator.hasNext()) { 206 fireProgressListenerStep(); 207 item = (UndoItem) redoIterator.next(); 208 item.redo(); 209 if (item instanceof ExternalUndoItem) { 210 addItem(item); 211 } else { 212 modifiedResources.add(manager.getFileObject(((UndoManager.ResourceUndoItem)item).getResource())); 214 } 215 } 216 fail = false; 217 } catch (RuntimeException e) { 218 e.printStackTrace(); 219 } finally { 220 try { 221 wasRedo = false; 222 JavaMetamodel.getDefaultRepository().endTrans(fail); 223 transactionEnded(fail); 224 } finally { 225 fireProgressListenerStop(); 226 fireStateChange(); 227 } 228 } 229 } 230 } 231 232 233 public void clear() { 234 undoList.clear(); 235 redoList.clear(); 236 descriptionMap.clear(); 237 fireStateChange(); 238 } 239 240 241 public void addItem(Resource r, ResourceImpl.DiffList l) { 242 addItem(new ResourceUndoItem(r,l)); 243 } 244 245 246 public void addItem(ExternalChange change) { 247 addItem(new ExternalUndoItem(change)); 248 } 249 250 251 public void addItem(UndoItem item) { 252 if (wasUndo) { 253 LinkedList redo = (LinkedList) this.redoList.getFirst(); 254 redo.addFirst(item); 255 } else { 256 if (transactionStart) { 257 undoList.addFirst(new LinkedList()); 258 descriptionMap.put(undoList.getFirst(), description); 259 transactionStart = false; 260 } 261 LinkedList undo = (LinkedList) this.undoList.getFirst(); 262 undo.addFirst(item); 263 } 264 if (! (wasUndo || wasRedo)) 265 redoList.clear(); 266 } 267 268 public boolean isUndoAvailable() { 269 return !undoList.isEmpty(); 270 } 271 272 public boolean isRedoAvailable() { 273 return !redoList.isEmpty(); 274 } 275 276 public void addPropertyChangeListener(PropertyChangeListener pcl) { 277 pcs.addPropertyChangeListener(pcl); 278 } 279 280 public void removePropertyChangeListener(PropertyChangeListener pcl) { 281 pcs.removePropertyChangeListener(pcl); 282 } 283 284 private void fireStateChange() { 285 pcs.firePropertyChange(PROP_STATE, null, null); 286 } 287 288 public void watch(Collection ceSupports, InvalidationListener l) { 289 synchronized (allCES) { 290 registerListeners(); 291 } 292 for (Iterator it = ceSupports.iterator(); it.hasNext();) { 293 final CloneableEditorSupport ces = (CloneableEditorSupport) it.next(); 294 final Document d = ces.getDocument(); 295 if (d!=null) { 296 NbDocument.runAtomic((StyledDocument )d, new Runnable () { 297 public void run() { 298 synchronized(allCES) { 299 if (allCES.add(ces)) { 300 ces.addChangeListener(UndoManager.this); 301 d.addDocumentListener(UndoManager.this); 302 documentToCES.put(d, ces); 303 } 304 } 305 } 306 }); 307 } else { 308 synchronized(allCES) { 309 if (allCES.add(ces)) { 310 ces.addChangeListener(UndoManager.this); 311 } 312 } 313 } 314 } 315 synchronized(allCES) { 316 if (l != null) { 317 listenerToCES.put(l, ceSupports); 318 } 319 } 320 } 321 322 public void stopWatching(InvalidationListener l) { 323 synchronized (allCES) { 325 listenerToCES.remove(l); 326 clearIfPossible(); 327 } 328 } 330 331 public void pathsAdded(GlobalPathRegistryEvent event) { 332 } 333 334 public void pathsRemoved(GlobalPathRegistryEvent event) { 335 assert event != null : "event == null"; if (event.getId().equals(ClassPath.SOURCE)) { 337 clear(); 338 } 339 } 340 341 private void registerListeners() { 342 if (listenersRegistered) return; 343 GlobalPathRegistry.getDefault().addGlobalPathRegistryListener(this); 344 Util.addFileSystemsListener(this); 345 for (Iterator it = allCES.iterator(); it.hasNext();) { 346 ((CloneableEditorSupport) it.next()).addChangeListener(this); 347 } 348 for (Iterator it = documentToCES.keySet().iterator(); it.hasNext();) { 349 ((Document ) it.next()).addDocumentListener(this); 350 } 351 listenersRegistered = true; 352 } 353 354 private void unregisterListeners() { 355 if (!listenersRegistered) return; 356 Util.removeFileSystemsListener(this); 357 GlobalPathRegistry.getDefault().removeGlobalPathRegistryListener(this); 358 for (Iterator it = allCES.iterator(); it.hasNext();) { 359 ((CloneableEditorSupport) it.next()).removeChangeListener(this); 360 } 361 for (Iterator it = documentToCES.keySet().iterator(); it.hasNext();) { 362 ((Document ) it.next()).removeDocumentListener(this); 363 } 364 listenersRegistered = false; 365 } 366 367 private void invalidate(CloneableEditorSupport ces) { 368 synchronized (undoList) { 369 if (!(wasRedo || wasUndo) && !dontDeleteUndo) { 370 clear(); 371 } 372 synchronized (allCES) { 373 if (ces == null) { 374 for (Iterator it = listenerToCES.keySet().iterator(); it.hasNext();) { 376 ((InvalidationListener) it.next()).invalidateObject(); 377 } 378 listenerToCES.clear(); 379 } else { 380 for (Iterator it = listenerToCES.entrySet().iterator(); it.hasNext();) { 381 Map.Entry e = (Map.Entry) it.next(); 382 if (((HashSet) e.getValue()).contains(ces)) { 383 ((InvalidationListener) e.getKey()).invalidateObject(); 384 it.remove(); 385 } 386 } 387 395 } 396 clearIfPossible(); 397 } 398 } 399 } 400 401 private void clearIfPossible() { 402 if (listenerToCES.isEmpty() && undoList.isEmpty() && redoList.isEmpty()) { 403 unregisterListeners(); 404 allCES.clear(); 405 documentToCES.clear(); 406 } 407 } 408 409 411 public void fileChanged(FileEvent fe) { 412 FileObject file = fe.getFile(); 413 if (!Util.isJavaFile(file)) { 414 return; 415 } 416 if (file != null) { 417 DataObject obj; 418 try { 419 obj = DataObject.find(file); 420 } catch (DataObjectNotFoundException e) { 421 return; 422 } 423 EditorCookie ec = (EditorCookie) obj.getCookie(EditorCookie.class); 424 if (ec != null) { 425 CloneableEditorSupport ces = (CloneableEditorSupport) documentToCES.get(ec.getDocument()); 426 if (ces != null) { 427 invalidate(ces); 428 } 429 } 430 } 431 } 432 433 public void fileDeleted(FileEvent fe) { 434 if (Util.isJavaFile(fe.getFile(), true)) { invalidate(null); 436 } 437 } 438 439 public void fileRenamed(FileRenameEvent fe) { 440 if (Util.isJavaFile(fe.getFile(), true)) { invalidate(null); 442 } 443 } 444 445 447 public void changedUpdate(DocumentEvent e) { 448 } 449 450 public void insertUpdate(DocumentEvent e) { 451 invalidate((CloneableEditorSupport) documentToCES.get(e.getDocument())); 452 } 453 454 public void removeUpdate(DocumentEvent e) { 455 invalidate((CloneableEditorSupport) documentToCES.get(e.getDocument())); 456 } 457 458 public void stateChanged(ChangeEvent e) { 459 synchronized (allCES) { 460 CloneableEditorSupport ces = (CloneableEditorSupport) e.getSource(); 461 Document d = ces.getDocument(); 462 for (Iterator it = documentToCES.entrySet().iterator(); it.hasNext();) { 463 Map.Entry en = (Map.Entry) it.next(); 464 if (en.getValue() == ces) { 465 ((Document ) en.getKey()).removeDocumentListener(this); 466 it.remove(); 467 break; 468 } 469 } 470 if (d != null) { 471 documentToCES.put(d, ces); 472 d.addDocumentListener(this); 473 } 474 } 475 } 476 477 public void saveAll() { 478 synchronized (allCES) { 479 unregisterListeners(); 480 } 481 try { 482 LifecycleManager.getDefault().saveAll(); 483 } finally { 484 synchronized (allCES) { 485 registerListeners(); 486 } 487 } 488 } 489 490 private void parseModified() { 491 ExclusiveMutex mutex = manager.getTransactionMutex(); 492 493 for (Iterator i = modifiedResources.iterator(); i.hasNext();) { 494 FileObject fo = (FileObject) i.next(); 495 if (fo.isValid()) { 496 mutex.addModified(fo); 497 manager.getDefaultRepository().beginTrans(true); 498 manager.getDefaultRepository().endTrans(); 499 } 500 } 501 502 for (Iterator i = modifiedResources.iterator(); i.hasNext();) { 504 FileObject fo = (FileObject) i.next(); 505 if (fo.isValid()) { 506 mutex.addModified(fo); 507 manager.getDefaultRepository().beginTrans(true); 508 manager.getDefaultRepository().endTrans(); 509 } 510 } 511 modifiedResources.clear(); 512 } 513 514 private void fireProgressListenerStart(int type, int count) { 515 stepCounter = 0; 516 if (progress == null) 517 return; 518 progress.start(new ProgressEvent(this, ProgressEvent.START, type, count)); 519 } 520 521 private int stepCounter = 0; 522 524 private void fireProgressListenerStep() { 525 if (progress == null) 526 return; 527 progress.step(new ProgressEvent(this, ProgressEvent.STEP, 0, ++stepCounter)); 528 } 529 530 532 private void fireProgressListenerStop() { 533 if (progress == null) 534 return; 535 progress.stop(new ProgressEvent(this, ProgressEvent.STOP)); 536 } 537 538 private interface UndoItem { 539 void undo(); 540 void redo(); 541 } 542 543 private final class ResourceUndoItem implements UndoItem { 544 private JavaModelPackage model; 545 private String resourceName = null; 546 private ResourceImpl.DiffList diffList; 547 548 public ResourceUndoItem(Resource r, ResourceImpl.DiffList l) { 549 model = (JavaModelPackage) r.refImmediatePackage(); 550 resourceName = r.getName(); 551 diffList = l; 552 } 553 554 558 public ResourceImpl.DiffList getDiffList() { 559 return diffList; 560 } 561 562 566 public void setDiffList(ResourceImpl.DiffList diffList) { 567 this.diffList = diffList; 568 } 569 570 574 public Resource getResource() { 575 return model.getResource().resolveResource(resourceName, false); 576 } 577 578 582 public void setResource(Resource resource) { 583 model = (JavaModelPackage) resource.refImmediatePackage(); 584 resourceName = resource.getName(); 585 } 586 587 public void undo() { 588 applyDiff(); 589 } 590 591 private void applyDiff() { 592 ((ResourceImpl) getResource()).applyDiff(diffList); 593 } 594 595 public void redo() { 596 applyDiff(); 597 } 598 } 599 600 private final class ExternalUndoItem implements UndoItem { 601 602 private ExternalChange change; 603 604 public ExternalUndoItem (ExternalChange change) { 605 this.change = change; 606 } 607 608 public void undo() { 609 change.undoExternalChange(); 610 } 611 612 public void redo() { 613 change.performExternalChange(); 614 } 615 } 616 617 } 618 | Popular Tags |