|
What this is
Other links
The source code/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun * Microsystems, Inc. All Rights Reserved. */ /* * UndoManager.java * * Created on 23. leden 2004, 16:24 */ package org.netbeans.modules.javacore.internalapi; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.*; import javax.swing.event.*; import javax.swing.text.Document; import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.api.java.classpath.GlobalPathRegistry; import org.netbeans.api.java.classpath.GlobalPathRegistryEvent; import org.netbeans.api.java.classpath.GlobalPathRegistryListener; import org.netbeans.jmi.javamodel.JavaModelPackage; import org.netbeans.jmi.javamodel.Resource; import org.netbeans.modules.javacore.ExclusiveMutex; import org.netbeans.modules.javacore.JMManager; import org.netbeans.modules.javacore.RepositoryUpdater; import org.netbeans.modules.javacore.Util; import org.netbeans.modules.javacore.jmiimpl.javamodel.ResourceImpl; import org.openide.LifecycleManager; import org.openide.cookies.EditorCookie; import org.openide.filesystems.*; import org.openide.loaders.DataObject; import org.openide.loaders.DataObjectNotFoundException; import org.openide.text.CloneableEditorSupport; /** * * @author Jan Becicka */ public class UndoManager extends FileChangeAdapter implements DocumentListener, RepositoryListener, ChangeListener, GlobalPathRegistryListener { /** stack of undo items */ private LinkedList undoList; /** stack of redo items */ private LinkedList redoList; /** set of all CloneableEditorSupports */ private final HashSet allCES = new HashSet(); /** map document -> CloneableEditorSupport */ private final HashMap documentToCES = new HashMap(); /** map document -> CloneableEditorSupport */ private final HashMap listenerToCES = new HashMap(); private boolean listenersRegistered = false; public static final String PROP_STATE = "state"; //NOI18N private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private boolean wasUndo = false; private boolean wasRedo = false; private boolean transactionStart; private boolean dontDeleteUndo = false; private IdentityHashMap descriptionMap; private String description; private HashSet modifiedResources; private JMManager manager; private ProgressListener progress; /** Creates a new instance of UndoManager */ public UndoManager() { undoList = new LinkedList(); redoList = new LinkedList(); descriptionMap = new IdentityHashMap(); modifiedResources = new HashSet(); manager = (JMManager) JMManager.getManager(); } public UndoManager(ProgressListener progress) { this(); this.progress = progress; } public void setUndoDescription(String desc) { description = desc; } public String getUndoDescription() { if (undoList.isEmpty()) return null; return (String) descriptionMap.get(undoList.getFirst()); } public String getRedoDescription() { if (redoList.isEmpty()) return null; return (String) descriptionMap.get(redoList.getFirst()); } /** called to mark transaction start */ public void transactionStarted() { modifiedResources.clear(); transactionStart = true; unregisterListeners(); RepositoryUpdater.getDefault().setListenOnChanges(false); } /** * called to mark end of transaction */ public void transactionEnded(boolean fail) { try { description = null; parseModified(); if (fail && !undoList.isEmpty()) undoList.removeFirst(); else { // [TODO] (jb) this code disables undos for changes using org.openide.src if (isUndoAvailable() && getUndoDescription() == null) { descriptionMap.remove(undoList.removeFirst()); clearIfPossible(); } } dontDeleteUndo = true; invalidate(null); dontDeleteUndo = false; } finally { RepositoryUpdater.getDefault().setListenOnChanges(true); registerListeners(); } fireStateChange(); } /** undo last transaction */ public void undo() { //System.out.println("************* Starting UNDO"); if (isUndoAvailable()) { JavaMetamodel.getDefaultRepository().beginTrans(true); transactionStarted(); boolean fail = true; try { wasUndo = true; LinkedList undo = (LinkedList) undoList.getFirst(); fireProgressListenerStart(0, undo.size()); undoList.removeFirst(); Iterator undoIterator = undo.iterator(); UndoItem item; redoList.addFirst(new LinkedList()); descriptionMap.put(redoList.getFirst(), descriptionMap.remove(undo)); while (undoIterator.hasNext()) { fireProgressListenerStep(); item = (UndoItem) undoIterator.next(); item.undo(); if (item instanceof ExternalUndoItem) { addItem(item); } else { //item is ResourceUndoItem modifiedResources.add(manager.getDataObject(((UndoManager.ResourceUndoItem)item).getResource())); } } fail = false; } catch (RuntimeException e) { e.printStackTrace(); } finally { wasUndo = false; JavaMetamodel.getDefaultRepository().endTrans(fail); transactionEnded(fail); fireProgressListenerStop(); fireStateChange(); } } } /** redo last undo */ public void redo() { //System.out.println("************* Starting REDO"); if (isRedoAvailable()) { JavaMetamodel.getDefaultRepository().beginTrans(true); boolean fail = true; transactionStarted(); try { wasRedo = true; LinkedList redo = (LinkedList) redoList.getFirst(); fireProgressListenerStart(1, redo.size()); redoList.removeFirst(); Iterator redoIterator = redo.iterator(); UndoItem item; description = (String) descriptionMap.remove(redo); while (redoIterator.hasNext()) { fireProgressListenerStep(); item = (UndoItem) redoIterator.next(); item.redo(); if (item instanceof ExternalUndoItem) { addItem(item); } else { //item is ResourceUndoItem modifiedResources.add(manager.getDataObject(((UndoManager.ResourceUndoItem)item).getResource())); } } fail = false; } finally { wasRedo = false; JavaMetamodel.getDefaultRepository().endTrans(fail); transactionEnded(fail); fireProgressListenerStop(); fireStateChange(); } } } /** clean undo/redo stacks */ public void clear() { undoList.clear(); redoList.clear(); descriptionMap.clear(); fireStateChange(); } /** add new item to undo/redo list */ public void addItem(Resource r, List l) { addItem(new ResourceUndoItem(r,l)); } /** add new item to undo/redo list */ public void addItem(ExternalChange change) { addItem(new ExternalUndoItem(change)); } /** add new item to undo/redo list */ public void addItem(UndoItem item) { if (wasUndo) { LinkedList redo = (LinkedList) this.redoList.getFirst(); redo.addFirst(item); } else { if (transactionStart) { undoList.addFirst(new LinkedList()); descriptionMap.put(undoList.getFirst(), description); transactionStart = false; } LinkedList undo = (LinkedList) this.undoList.getFirst(); undo.addFirst(item); } if (! (wasUndo || wasRedo)) redoList.clear(); } public boolean isUndoAvailable() { return !undoList.isEmpty(); } public boolean isRedoAvailable() { return !redoList.isEmpty(); } public void addPropertyChangeListener(PropertyChangeListener pcl) { pcs.addPropertyChangeListener(pcl); } public void removePropertyChangeListener(PropertyChangeListener pcl) { pcs.removePropertyChangeListener(pcl); } private void fireStateChange() { pcs.firePropertyChange(PROP_STATE, null, null); } public void watch(Collection ceSupports, InvalidationListener l) { synchronized (allCES) { registerListeners(); for (Iterator it = ceSupports.iterator(); it.hasNext();) { CloneableEditorSupport ces = (CloneableEditorSupport) it.next(); if (allCES.add(ces)) { ces.addChangeListener(this); Document d = ces.getDocument(); if (d != null) { d.addDocumentListener(this); documentToCES.put(d, ces); } } } if (l != null) { listenerToCES.put(l, ceSupports); } } } public void stopWatching(InvalidationListener l) { //synchronized (undoStack) { synchronized (allCES) { listenerToCES.remove(l); clearIfPossible(); } //} } public void pathsAdded(GlobalPathRegistryEvent event) { } public void pathsRemoved(GlobalPathRegistryEvent event) { assert event != null : "event == null"; // NOI18N if (event.getId().equals(ClassPath.SOURCE)) { clear(); } } private void registerListeners() { if (listenersRegistered) return; GlobalPathRegistry.getDefault().addGlobalPathRegistryListener(this); Util.addFileSystemsListener(this); Repository.getDefault().addRepositoryListener(this); for (Iterator it = allCES.iterator(); it.hasNext();) { ((CloneableEditorSupport) it.next()).addChangeListener(this); } for (Iterator it = documentToCES.keySet().iterator(); it.hasNext();) { ((Document) it.next()).addDocumentListener(this); } listenersRegistered = true; } private void unregisterListeners() { if (!listenersRegistered) return; Util.removeFileSystemsListener(this); GlobalPathRegistry.getDefault().removeGlobalPathRegistryListener(this); Repository.getDefault().removeRepositoryListener(this); for (Iterator it = allCES.iterator(); it.hasNext();) { ((CloneableEditorSupport) it.next()).removeChangeListener(this); } for (Iterator it = documentToCES.keySet().iterator(); it.hasNext();) { ((Document) it.next()).removeDocumentListener(this); } listenersRegistered = false; } private void invalidate(CloneableEditorSupport ces) { synchronized (undoList) { if (!(wasRedo || wasUndo) && !dontDeleteUndo) { clear(); } synchronized (allCES) { if (ces == null) { // invalidate all for (Iterator it = listenerToCES.keySet().iterator(); it.hasNext();) { ((InvalidationListener) it.next()).invalidateObject(); } listenerToCES.clear(); } else { for (Iterator it = listenerToCES.entrySet().iterator(); it.hasNext();) { Map.Entry e = (Map.Entry) it.next(); if (((HashSet) e.getValue()).contains(ces)) { ((InvalidationListener) e.getKey()).invalidateObject(); it.remove(); } } /*ces.removeChangeListener(this); allCES.remove(ces); Document d = ces.getDocument(); if (d != null) { d.removeDocumentListener(this); documentToCES.remove(d); } */ } clearIfPossible(); } } } private void clearIfPossible() { if (listenerToCES.isEmpty() && undoList.isEmpty() && redoList.isEmpty()) { //unregisterListeners(); allCES.clear(); documentToCES.clear(); } } // FileChangeAdapter ........................................................ public void fileChanged(FileEvent fe) { FileObject file = fe.getFile(); if (!Util.isJavaFile(file)) { return; } if (file != null) { DataObject obj; try { obj = DataObject.find(file); } catch (DataObjectNotFoundException e) { return; } EditorCookie ec = (EditorCookie) obj.getCookie(EditorCookie.class); if (ec != null) { CloneableEditorSupport ces = (CloneableEditorSupport) documentToCES.get(ec.getDocument()); if (ces != null) { invalidate(ces); } } } } public void fileDeleted(FileEvent fe) { if (Util.isJavaFile(fe.getFile())) { // NOI18N invalidate(null); } //fileChanged(fe); } public void fileRenamed(FileRenameEvent fe) { fileChanged(fe); } // DocumentListener ......................................................... public void changedUpdate(DocumentEvent e) { } public void insertUpdate(DocumentEvent e) { invalidate((CloneableEditorSupport) documentToCES.get(e.getDocument())); } public void removeUpdate(DocumentEvent e) { invalidate((CloneableEditorSupport) documentToCES.get(e.getDocument())); } // RepositoryListener ....................................................... public void fileSystemAdded(RepositoryEvent ev) { } public void fileSystemPoolReordered(RepositoryReorderedEvent ev) { } public void fileSystemRemoved(RepositoryEvent ev) { invalidate(null); } public void stateChanged(ChangeEvent e) { synchronized (allCES) { CloneableEditorSupport ces = (CloneableEditorSupport) e.getSource(); Document d = ces.getDocument(); for (Iterator it = documentToCES.entrySet().iterator(); it.hasNext();) { Map.Entry en = (Map.Entry) it.next(); if (en.getValue() == ces) { ((Document) en.getKey()).removeDocumentListener(this); it.remove(); break; } } if (d != null) { documentToCES.put(d, ces); d.addDocumentListener(this); } } } public void saveAll() { synchronized (allCES) { unregisterListeners(); try { LifecycleManager.getDefault().saveAll(); } finally { registerListeners(); } } } private void parseModified() { ExclusiveMutex mutex = manager.getTransactionMutex(); for (Iterator i = modifiedResources.iterator(); i.hasNext();) { mutex.addModified((DataObject) i.next()); } manager.getDefaultRepository().beginTrans(true); manager.getDefaultRepository().endTrans(); //resources must be parsed twice to avoid #48913 for (Iterator i = modifiedResources.iterator(); i.hasNext();) { mutex.addModified((DataObject) i.next()); } modifiedResources.clear(); manager.getDefaultRepository().beginTrans(true); manager.getDefaultRepository().endTrans(); } private void fireProgressListenerStart(int type, int count) { stepCounter = 0; if (progress == null) return; progress.start(new ProgressEvent(this, ProgressEvent.START, type, count)); } private int stepCounter = 0; /** Notifies all registered listeners about the event. */ private void fireProgressListenerStep() { if (progress == null) return; progress.step(new ProgressEvent(this, ProgressEvent.STEP, 0, ++stepCounter)); } /** Notifies all registered listeners about the event. */ private void fireProgressListenerStop() { if (progress == null) return; progress.stop(new ProgressEvent(this, ProgressEvent.STOP)); } private interface UndoItem { void undo(); void redo(); } private final class ResourceUndoItem implements UndoItem { private JavaModelPackage model; private String resourceName = null; private List diffList; public ResourceUndoItem(Resource r, List l) { model = (JavaModelPackage) r.refImmediatePackage(); resourceName = r.getName(); diffList = l; } /** * Getter for property diffList. * @return Value of property diffList. */ public List getDiffList() { return diffList; } /** * Setter for property diffList. * @param diffList New value of property diffList. */ public void setDiffList(List diffList) { this.diffList = diffList; } /** * Getter for property resource. * @return Value of property resource. */ public Resource getResource() { return model.getResource().resolveResource(resourceName, false); } /** * Setter for property resource. * @param resource New value of property resource. */ public void setResource(Resource resource) { model = (JavaModelPackage) resource.refImmediatePackage(); resourceName = resource.getName(); } public void undo() { applyDiff(); } private void applyDiff() { ((ResourceImpl) getResource()).applyDiff(diffList, false, false); } public void redo() { applyDiff(); } } private final class ExternalUndoItem implements UndoItem { private ExternalChange change; public ExternalUndoItem (ExternalChange change) { this.change = change; } public void undo() { change.undoExternalChange(); } public void redo() { change.performExternalChange(); } } } |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.