KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > javacore > internalapi > 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-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.javacore.internalapi;
21
22 import java.beans.PropertyChangeListener JavaDoc;
23 import java.beans.PropertyChangeSupport JavaDoc;
24 import java.util.*;
25 import javax.swing.event.*;
26 import javax.swing.text.Document JavaDoc;
27 import javax.swing.text.StyledDocument JavaDoc;
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 /**
42  *
43  * @author Jan Becicka
44  */

45 public class UndoManager extends FileChangeAdapter implements DocumentListener, ChangeListener JavaDoc, GlobalPathRegistryListener {
46     
47     /** stack of undo items */
48     private LinkedList undoList;
49     
50     /** stack of redo items */
51     private LinkedList redoList;
52
53     /** set of all CloneableEditorSupports */
54     private final HashSet allCES = new HashSet();
55     
56     /** map document -> CloneableEditorSupport */
57     private final HashMap documentToCES = new HashMap();
58     
59     /** map document -> CloneableEditorSupport */
60     private final HashMap listenerToCES = new HashMap();
61     private boolean listenersRegistered = false;
62     
63     public static final String JavaDoc PROP_STATE = "state"; //NOI18N
64

65     private final PropertyChangeSupport JavaDoc pcs = new PropertyChangeSupport JavaDoc(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 JavaDoc description;
74     private HashSet modifiedResources;
75     private JMManager manager;
76     private ProgressListener progress;
77
78
79     /** Creates a new instance of UndoManager */
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 JavaDoc desc) {
94         description = desc;
95     }
96     
97     public String JavaDoc getUndoDescription() {
98         if (undoList.isEmpty()) return null;
99         return (String JavaDoc) descriptionMap.get(undoList.getFirst());
100     }
101     
102     public String JavaDoc getRedoDescription() {
103         if (redoList.isEmpty()) return null;
104         return (String JavaDoc) descriptionMap.get(redoList.getFirst());
105     }
106     
107     /** called to mark transaction start
108      */

109     public void transactionStarted() {
110         modifiedResources.clear();
111         transactionStart = true;
112         unregisterListeners();
113         RepositoryUpdater.getDefault().setListenOnChanges(false);
114     }
115     
116     /**
117      * called to mark end of transaction
118      */

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                 // [TODO] (jb) this code disables undos for changes using org.openide.src
128
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     /** undo last transaction */
145     public void undo() {
146         //System.out.println("************* Starting UNDO");
147
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                         //item is ResourceUndoItem
169
modifiedResources.add(manager.getFileObject(((UndoManager.ResourceUndoItem)item).getResource()));
170                     }
171                 }
172                 fail = false;
173             } catch (RuntimeException JavaDoc 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     /** redo last undo
189      */

190     public void redo() {
191         //System.out.println("************* Starting REDO");
192
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 JavaDoc) 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                         //item is ResourceUndoItem
213
modifiedResources.add(manager.getFileObject(((UndoManager.ResourceUndoItem)item).getResource()));
214                     }
215                 }
216                 fail = false;
217             } catch (RuntimeException JavaDoc 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     /** clean undo/redo stacks */
233     public void clear() {
234         undoList.clear();
235         redoList.clear();
236         descriptionMap.clear();
237         fireStateChange();
238     }
239     
240     /** add new item to undo/redo list */
241     public void addItem(Resource r, ResourceImpl.DiffList l) {
242         addItem(new ResourceUndoItem(r,l));
243     }
244     
245     /** add new item to undo/redo list */
246     public void addItem(ExternalChange change) {
247         addItem(new ExternalUndoItem(change));
248     }
249     
250     /** add new item to undo/redo list */
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 JavaDoc pcl) {
277         pcs.addPropertyChangeListener(pcl);
278     }
279     
280     public void removePropertyChangeListener(PropertyChangeListener JavaDoc 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 JavaDoc d = ces.getDocument();
295             if (d!=null) {
296                 NbDocument.runAtomic((StyledDocument JavaDoc)d, new Runnable JavaDoc() {
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 (undoStack) {
324
synchronized (allCES) {
325                 listenerToCES.remove(l);
326                 clearIfPossible();
327             }
328         //}
329
}
330     
331     public void pathsAdded(GlobalPathRegistryEvent event) {
332     }
333
334     public void pathsRemoved(GlobalPathRegistryEvent event) {
335         assert event != null : "event == null"; // NOI18N
336
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 JavaDoc) 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 JavaDoc) 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                     // invalidate all
375
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                     /*ces.removeChangeListener(this);
388                     allCES.remove(ces);
389                     Document d = ces.getDocument();
390                     if (d != null) {
391                         d.removeDocumentListener(this);
392                         documentToCES.remove(d);
393                     }
394                      */

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     // FileChangeAdapter ........................................................
410

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)) { // NOI18N
435
invalidate(null);
436         }
437     }
438
439     public void fileRenamed(FileRenameEvent fe) {
440         if (Util.isJavaFile(fe.getFile(), true)) { // NOI18N
441
invalidate(null);
442         }
443     }
444     
445     // DocumentListener .........................................................
446

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 JavaDoc 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 JavaDoc) 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         //resources must be parsed twice to avoid #48913
503
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     /** Notifies all registered listeners about the event.
523      */

524     private void fireProgressListenerStep() {
525         if (progress == null)
526             return;
527         progress.step(new ProgressEvent(this, ProgressEvent.STEP, 0, ++stepCounter));
528     }
529
530     /** Notifies all registered listeners about the event.
531      */

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 JavaDoc 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         /**
555          * Getter for property diffList.
556          * @return Value of property diffList.
557          */

558         public ResourceImpl.DiffList getDiffList() {
559             return diffList;
560         }
561         
562         /**
563          * Setter for property diffList.
564          * @param diffList New value of property diffList.
565          */

566         public void setDiffList(ResourceImpl.DiffList diffList) {
567             this.diffList = diffList;
568         }
569         
570         /**
571          * Getter for property resource.
572          * @return Value of property resource.
573          */

574         public Resource getResource() {
575             return model.getResource().resolveResource(resourceName, false);
576         }
577         
578         /**
579          * Setter for property resource.
580          * @param resource New value of property resource.
581          */

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