KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > refactoring > spi > impl > UndoManager


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-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.refactoring.spi.impl;
21
22 import java.beans.PropertyChangeListener JavaDoc;
23 import java.beans.PropertyChangeSupport JavaDoc;
24 import java.util.Collection JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.IdentityHashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.LinkedList JavaDoc;
30 import java.util.Map JavaDoc;
31 import javax.swing.event.ChangeEvent JavaDoc;
32 import javax.swing.event.ChangeListener JavaDoc;
33 import javax.swing.event.DocumentEvent JavaDoc;
34 import javax.swing.event.DocumentListener JavaDoc;
35 import javax.swing.text.Document JavaDoc;
36 import javax.swing.text.StyledDocument JavaDoc;
37 import org.netbeans.modules.refactoring.spi.BackupFacility;
38 import org.netbeans.modules.refactoring.api.ProgressEvent;
39 import org.netbeans.modules.refactoring.api.ProgressListener;
40 import org.netbeans.modules.refactoring.api.RefactoringSession;
41 import org.openide.LifecycleManager;
42 import org.openide.cookies.EditorCookie;
43 import org.openide.filesystems.FileChangeAdapter;
44 import org.openide.filesystems.FileEvent;
45 import org.openide.filesystems.FileObject;
46 import org.openide.filesystems.FileRenameEvent;
47 import org.openide.loaders.DataObject;
48 import org.openide.loaders.DataObjectNotFoundException;
49 import org.openide.text.CloneableEditorSupport;
50 import org.openide.text.NbDocument;
51
52 /**
53  *
54  * @author Jan Becicka
55  */

56 public class UndoManager extends FileChangeAdapter implements DocumentListener JavaDoc, ChangeListener JavaDoc/*, GlobalPathRegistryListener */{
57     
58     /** stack of undo items */
59     private LinkedList JavaDoc<LinkedList JavaDoc<UndoItem>> undoList;
60     
61     /** stack of redo items */
62     private LinkedList JavaDoc<LinkedList JavaDoc<UndoItem>> redoList;
63
64     /** set of all CloneableEditorSupports */
65     private final HashSet JavaDoc<CloneableEditorSupport> allCES = new HashSet JavaDoc();
66     
67     /** map document -> CloneableEditorSupport */
68     private final HashMap JavaDoc<Document JavaDoc, CloneableEditorSupport> documentToCES = new HashMap JavaDoc();
69     
70     /** map listener -> CloneableEditorSupport */
71     private final HashMap JavaDoc<InvalidationListener, Collection JavaDoc<? extends CloneableEditorSupport>> listenerToCES = new HashMap JavaDoc();
72     private boolean listenersRegistered = false;
73     
74     public static final String JavaDoc PROP_STATE = "state"; //NOI18N
75

76     private final PropertyChangeSupport JavaDoc pcs = new PropertyChangeSupport JavaDoc(this);
77     
78     private boolean wasUndo = false;
79     private boolean wasRedo = false;
80     private boolean transactionStart;
81     private boolean dontDeleteUndo = false;
82     
83     private IdentityHashMap JavaDoc<LinkedList JavaDoc, String JavaDoc> descriptionMap;
84     private String JavaDoc description;
85     private ProgressListener progress;
86     
87     private static UndoManager instance;
88
89
90     public static UndoManager getDefault() {
91         if (instance==null) {
92             instance = new UndoManager();
93         }
94         return instance;
95     }
96     /** Creates a new instance of UndoManager */
97     private UndoManager() {
98         undoList = new LinkedList JavaDoc();
99         redoList = new LinkedList JavaDoc();
100         descriptionMap = new IdentityHashMap JavaDoc();
101     }
102     
103     private UndoManager(ProgressListener progress) {
104         this();
105         this.progress = progress;
106     }
107     
108     public void setUndoDescription(String JavaDoc desc) {
109         description = desc;
110     }
111     
112     public String JavaDoc getUndoDescription() {
113         if (undoList.isEmpty()) return null;
114         return descriptionMap.get(undoList.getFirst());
115     }
116     
117     public String JavaDoc getRedoDescription() {
118         if (redoList.isEmpty()) return null;
119         return descriptionMap.get(redoList.getFirst());
120     }
121     
122     /** called to mark transaction start
123      */

124     public void transactionStarted() {
125         transactionStart = true;
126         unregisterListeners();
127         //RepositoryUpdater.getDefault().setListenOnChanges(false);
128
}
129     
130     /**
131      * called to mark end of transaction
132      */

133     public void transactionEnded(boolean fail) {
134         try {
135             description = null;
136             dontDeleteUndo = true;
137             if (fail && !undoList.isEmpty())
138                 undoList.removeFirst();
139             else {
140                 // [TODO] (jb) this code disables undos for changes using org.openide.src
141
if (isUndoAvailable() && getUndoDescription() == null) {
142                     descriptionMap.remove(undoList.removeFirst());
143                     dontDeleteUndo = false;
144                 }
145                 
146             }
147             
148             invalidate(null);
149             dontDeleteUndo = false;
150         } finally {
151             registerListeners();
152         }
153         fireStateChange();
154     }
155     
156     /** undo last transaction */
157     public void undo() {
158         //System.out.println("************* Starting UNDO");
159
if (isUndoAvailable()) {
160             boolean fail = true;
161             try {
162                 transactionStarted();
163                 wasUndo = true;
164                 LinkedList JavaDoc undo = (LinkedList JavaDoc) undoList.getFirst();
165                 fireProgressListenerStart(0, undo.size());
166                 undoList.removeFirst();
167                 Iterator JavaDoc undoIterator = undo.iterator();
168                 UndoItem item;
169                 redoList.addFirst(new LinkedList JavaDoc());
170                 descriptionMap.put(redoList.getFirst(), descriptionMap.remove(undo));
171                 while (undoIterator.hasNext()) {
172                     fireProgressListenerStep();
173                     item = (UndoItem) undoIterator.next();
174                     item.undo();
175                     if (item instanceof SessionUndoItem) {
176                         addItem(item);
177                     }
178                 }
179                 fail = false;
180             } finally {
181                 try {
182                     wasUndo = false;
183                     transactionEnded(fail);
184                 } finally {
185                     fireProgressListenerStop();
186                     fireStateChange();
187                 }
188             }
189         }
190     }
191     
192     /** redo last undo
193      */

194     public void redo() {
195         //System.out.println("************* Starting REDO");
196
if (isRedoAvailable()) {
197             boolean fail = true;
198             try {
199                 transactionStarted();
200                 wasRedo = true;
201                 LinkedList JavaDoc redo = redoList.getFirst();
202                 fireProgressListenerStart(1, redo.size());
203                 redoList.removeFirst();
204                 Iterator JavaDoc<UndoItem> redoIterator = redo.iterator();
205                 UndoItem item;
206                 description = descriptionMap.remove(redo);
207                 while (redoIterator.hasNext()) {
208                     fireProgressListenerStep();
209                     item = redoIterator.next();
210                     item.redo();
211                     if (item instanceof SessionUndoItem) {
212                         addItem(item);
213                     }
214                 }
215                 fail = false;
216             } finally {
217                 try {
218                     wasRedo = false;
219                     transactionEnded(fail);
220                 } finally {
221                     fireProgressListenerStop();
222                     fireStateChange();
223                 }
224             }
225         }
226     }
227     
228     /** clean undo/redo stacks */
229     public void clear() {
230         undoList.clear();
231         redoList.clear();
232         descriptionMap.clear();
233         BackupFacility.getDefault().clear();
234         fireStateChange();
235     }
236     
237     public void addItem(RefactoringSession session) {
238         addItem(new SessionUndoItem(session));
239     }
240     
241     /** add new item to undo/redo list */
242     public void addItem(UndoItem item) {
243         if (wasUndo) {
244             LinkedList JavaDoc redo = this.redoList.getFirst();
245             redo.addFirst(item);
246         } else {
247             if (transactionStart) {
248                 undoList.addFirst(new LinkedList JavaDoc());
249                 descriptionMap.put(undoList.getFirst(), description);
250                 transactionStart = false;
251             }
252             LinkedList JavaDoc undo = this.undoList.getFirst();
253             undo.addFirst(item);
254         }
255         if (! (wasUndo || wasRedo))
256             redoList.clear();
257     }
258      
259     public boolean isUndoAvailable() {
260         return !undoList.isEmpty();
261     }
262     
263     public boolean isRedoAvailable() {
264         return !redoList.isEmpty();
265     }
266     
267     public void addPropertyChangeListener(PropertyChangeListener JavaDoc pcl) {
268         pcs.addPropertyChangeListener(pcl);
269     }
270     
271     public void removePropertyChangeListener(PropertyChangeListener JavaDoc pcl) {
272         pcs.removePropertyChangeListener(pcl);
273     }
274
275     private void fireStateChange() {
276         pcs.firePropertyChange(PROP_STATE, null, null);
277     }
278     
279     public void watch(Collection JavaDoc<? extends CloneableEditorSupport> ceSupports, InvalidationListener l) {
280         synchronized (allCES) {
281             registerListeners();
282         }
283         for (Iterator JavaDoc<? extends CloneableEditorSupport> it = ceSupports.iterator(); it.hasNext();) {
284             final CloneableEditorSupport ces = it.next();
285             final Document JavaDoc d = ces.getDocument();
286             if (d!=null) {
287                 NbDocument.runAtomic((StyledDocument JavaDoc)d, new Runnable JavaDoc() {
288                     public void run() {
289                         synchronized(allCES) {
290                             if (allCES.add(ces)) {
291                                 ces.addChangeListener(UndoManager.this);
292                                 d.addDocumentListener(UndoManager.this);
293                                 documentToCES.put(d, ces);
294                             }
295                         }
296                     }
297                 });
298             } else {
299                 synchronized(allCES) {
300                     if (allCES.add(ces)) {
301                         ces.addChangeListener(UndoManager.this);
302                     }
303                 }
304             }
305         }
306         synchronized(allCES) {
307             if (l != null) {
308                 listenerToCES.put(l, ceSupports);
309             }
310         }
311     }
312     
313     public void stopWatching(InvalidationListener l) {
314         //synchronized (undoStack) {
315
synchronized (allCES) {
316                 listenerToCES.remove(l);
317                 clearIfPossible();
318             }
319         //}
320
}
321     
322 // TODO:
323
// public void pathsAdded(GlobalPathRegistryEvent event) {
324
// }
325
//
326
// public void pathsRemoved(GlobalPathRegistryEvent event) {
327
// assert event != null : "event == null"; // NOI18N
328
// if (event.getId().equals(ClassPath.SOURCE)) {
329
// clear();
330
// }
331
// }
332

333     private void registerListeners() {
334         if (listenersRegistered) return;
335         // TODO:
336
// GlobalPathRegistry.getDefault().addGlobalPathRegistryListener(this);
337
Util.addFileSystemsListener(this);
338         for (CloneableEditorSupport ces:allCES) {
339             ces.addChangeListener(this);
340         }
341         for (Document JavaDoc doc:documentToCES.keySet()) {
342             doc.addDocumentListener(this);
343         }
344         listenersRegistered = true;
345     }
346     
347     private void unregisterListeners() {
348         if (!listenersRegistered) return;
349         Util.removeFileSystemsListener(this);
350         //TODO:
351
//GlobalPathRegistry.getDefault().removeGlobalPathRegistryListener(this);
352
for (CloneableEditorSupport ces:allCES) {
353             ces.removeChangeListener(this);
354         }
355         for (Document JavaDoc doc:documentToCES.keySet()) {
356             doc.removeDocumentListener(this);
357         }
358         listenersRegistered = false;
359     }
360     
361     private void invalidate(CloneableEditorSupport ces) {
362         synchronized (undoList) {
363             if (!(wasRedo || wasUndo) && !dontDeleteUndo) {
364                 clear();
365             }
366             synchronized (allCES) {
367                 if (ces == null) {
368                     // invalidate all
369
for (InvalidationListener lis:listenerToCES.keySet()) {
370                         lis.invalidateObject();
371                     }
372                     listenerToCES.clear();
373                 } else {
374                     for (Iterator JavaDoc<Map.Entry JavaDoc<InvalidationListener, Collection JavaDoc<? extends CloneableEditorSupport>>> it = listenerToCES.entrySet().iterator(); it.hasNext();) {
375                         Map.Entry JavaDoc<InvalidationListener, Collection JavaDoc<? extends CloneableEditorSupport>> e = it.next();
376                         if ((e.getValue()).contains(ces)) {
377                             e.getKey().invalidateObject();
378                             it.remove();
379                         }
380                     }
381                     /*ces.removeChangeListener(this);
382                     allCES.remove(ces);
383                     Document d = ces.getDocument();
384                     if (d != null) {
385                         d.removeDocumentListener(this);
386                         documentToCES.remove(d);
387                     }
388                      */

389                 }
390                 clearIfPossible();
391             }
392         }
393     }
394     
395     private void clearIfPossible() {
396         if (listenerToCES.isEmpty() && undoList.isEmpty() && redoList.isEmpty()) {
397             unregisterListeners();
398             allCES.clear();
399             documentToCES.clear();
400         }
401     }
402     
403     // FileChangeAdapter ........................................................
404

405     public void fileChanged(FileEvent fe) {
406         FileObject file = fe.getFile();
407         if (file != null) {
408             DataObject obj;
409             try {
410                 obj = DataObject.find(file);
411             } catch (DataObjectNotFoundException e) {
412                 return;
413             }
414             EditorCookie ec = obj.getCookie(EditorCookie.class);
415             if (ec != null) {
416                 CloneableEditorSupport ces = documentToCES.get(ec.getDocument());
417                 if (ces != null) {
418                     invalidate(ces);
419                 }
420             }
421         }
422     }
423     
424     public void fileDeleted(FileEvent fe) {
425         //if (Util.isJavaFile(fe.getFile(), true)) { // NOI18N
426
//TODO: invalidate(null);
427
//}
428
}
429
430     public void fileRenamed(FileRenameEvent fe) {
431         //if (Util.isJavaFile(fe.getFile(), true)) { // NOI18N
432
//TODO: invalidate(null);
433
//}
434
}
435     
436     // DocumentListener .........................................................
437

438     public void changedUpdate(DocumentEvent JavaDoc e) {
439     }
440
441     public void insertUpdate(DocumentEvent JavaDoc e) {
442         invalidate(documentToCES.get(e.getDocument()));
443     }
444
445     public void removeUpdate(DocumentEvent JavaDoc e) {
446         invalidate(documentToCES.get(e.getDocument()));
447     }
448         
449     public void stateChanged(ChangeEvent JavaDoc e) {
450         synchronized (allCES) {
451             CloneableEditorSupport ces = (CloneableEditorSupport) e.getSource();
452             Document JavaDoc d = ces.getDocument();
453             for (Iterator JavaDoc it = documentToCES.entrySet().iterator(); it.hasNext();) {
454                 Map.Entry JavaDoc en = (Map.Entry JavaDoc) it.next();
455                 if (en.getValue() == ces) {
456                     ((Document JavaDoc) en.getKey()).removeDocumentListener(this);
457                     it.remove();
458                     break;
459                 }
460             }
461             if (d != null) {
462                 documentToCES.put(d, ces);
463                 d.addDocumentListener(this);
464             }
465         }
466     }
467     
468     public void saveAll() {
469         synchronized (allCES) {
470             unregisterListeners();
471         }
472         try {
473             LifecycleManager.getDefault().saveAll();
474         } finally {
475             synchronized (allCES) {
476                 registerListeners();
477             }
478         }
479     }
480
481     private void fireProgressListenerStart(int type, int count) {
482         stepCounter = 0;
483         if (progress == null)
484             return;
485         progress.start(new ProgressEvent(this, ProgressEvent.START, type, count));
486     }
487     
488     private int stepCounter = 0;
489     /** Notifies all registered listeners about the event.
490      */

491     private void fireProgressListenerStep() {
492         if (progress == null)
493             return;
494         progress.step(new ProgressEvent(this, ProgressEvent.STEP, 0, ++stepCounter));
495     }
496
497     /** Notifies all registered listeners about the event.
498      */

499     private void fireProgressListenerStop() {
500         if (progress == null)
501             return;
502         progress.stop(new ProgressEvent(this, ProgressEvent.STOP));
503     }
504     
505     private interface UndoItem {
506         void undo();
507         void redo();
508     }
509     
510     private final class SessionUndoItem implements UndoItem {
511         
512          private RefactoringSession change;
513         
514         public SessionUndoItem (RefactoringSession change) {
515             this.change = change;
516         }
517         
518         public void undo() {
519             change.undoRefactoring(false);
520         }
521         
522         public void redo() {
523             change.doRefactoring(false);
524         }
525     }
526 }
527
Popular Tags