# This patch file was generated by NetBeans IDE # Following Index: paths are relative to: /Users/beci/source/jet-main # This patch can be applied using context Tools: Patch action on respective folder. # It uses platform neutral UTF-8 encoding and \n newlines. # Above lines and this line are ignored by the patching process. Index: diff/src/org/netbeans/api/diff/Difference.java --- diff/src/org/netbeans/api/diff/Difference.java Base (BASE) +++ diff/src/org/netbeans/api/diff/Difference.java Locally Modified (Based On LOCAL) @@ -62,6 +62,12 @@ /** Change type of difference - a portion of a file was changed in the other */ public static final int CHANGE = 2; + public static class Kind { + + public Kind() { + } + } + private int type = 0; private int firstStart = 0; private int firstEnd = 0; Index: editor.lib/nbproject/project.xml --- editor.lib/nbproject/project.xml Base (BASE) +++ editor.lib/nbproject/project.xml Locally Modified (Based On LOCAL) @@ -202,6 +202,11 @@ + org.netbeans.modules.editor.mimelookup + + + + org.netbeans.modules.editor.settings.storage Index: editor.lib/src/org/netbeans/editor/BaseDocument.java --- editor.lib/src/org/netbeans/editor/BaseDocument.java Base (BASE) +++ editor.lib/src/org/netbeans/editor/BaseDocument.java Locally Modified (Based On LOCAL) @@ -45,7 +45,6 @@ package org.netbeans.editor; import java.awt.Font; -import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; import java.util.Hashtable; import java.util.Dictionary; @@ -56,6 +55,7 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeSupport; +import java.util.Collection; import java.util.EventListener; import java.util.HashSet; import java.util.Set; @@ -112,11 +112,10 @@ import org.netbeans.modules.editor.lib2.document.ReadWriteBuffer; import org.netbeans.modules.editor.lib2.document.ReadWriteUtils; import org.netbeans.modules.editor.lib2.document.StableCompoundEdit; +import org.netbeans.spi.editor.document.UndoableEditWrapper; import org.netbeans.spi.lexer.MutableTextInput; import org.netbeans.spi.lexer.TokenHierarchyControl; import org.openide.filesystems.FileObject; -import org.openide.util.RequestProcessor; -import org.openide.util.RequestProcessor.Task; import org.openide.util.WeakListeners; /** @@ -346,6 +345,8 @@ private UndoableEdit removeUpdateLineUndo; + private Collection undoEditWrappers; + private DocumentFilter.FilterBypass filterBypass; private Preferences prefs; @@ -565,6 +566,8 @@ TrailingWhitespaceRemove.install(this); + undoEditWrappers = MimeLookup.getLookup(mimeType).lookupAll(UndoableEditWrapper.class); + if (weakPrefsListener == null) { // the listening could have already been initialized from setMimeType(), which // is called by some kits from initDocument() @@ -1571,6 +1574,18 @@ } protected @Override void fireUndoableEditUpdate(UndoableEditEvent e) { + // Possibly wrap contained edit + if (undoEditWrappers != null) { + UndoableEdit origEdit = e.getEdit(); + UndoableEdit edit = origEdit; + for (UndoableEditWrapper wrapper : undoEditWrappers) { + edit = wrapper.wrap(edit, this); + } + if (edit != origEdit) { + e = new UndoableEditEvent(this, edit); + } + } + // Fire to the list of listeners that was used before the atomic lock started // This fixes issue #47881 and appears to be somewhat more logical // than the default approach to fire all the current listeners Index: editor.lib/test/unit/src/org/netbeans/editor/TestingUndoableEditWrapper.java --- editor.lib/test/unit/src/org/netbeans/editor/TestingUndoableEditWrapper.java Base (BASE) +++ editor.lib/test/unit/src/org/netbeans/editor/TestingUndoableEditWrapper.java Locally New @@ -0,0 +1,72 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.editor; + +import javax.swing.text.Document; +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoableEdit; +import org.netbeans.spi.editor.document.UndoableEditWrapper; + +/** + * + * @author mmetelka + */ +//@MimeRegistration(mimeType="", service=UndoableEditWrapper.class) +public class TestingUndoableEditWrapper implements UndoableEditWrapper { + + @Override + public UndoableEdit wrap(UndoableEdit edit, Document doc) { + WrapCompoundEdit wrapEdit = new WrapCompoundEdit(); + wrapEdit.addEdit(edit); + wrapEdit.end(); + return wrapEdit; + } + + static final class WrapCompoundEdit extends CompoundEdit { + + WrapCompoundEdit() { + } + + } + + +} Index: editor.lib/test/unit/src/org/netbeans/editor/UndoableEditWrapperTest.java --- editor.lib/test/unit/src/org/netbeans/editor/UndoableEditWrapperTest.java Base (BASE) +++ editor.lib/test/unit/src/org/netbeans/editor/UndoableEditWrapperTest.java Locally New @@ -0,0 +1,80 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ + +package org.netbeans.editor; + +import javax.swing.event.UndoableEditEvent; +import javax.swing.event.UndoableEditListener; +import javax.swing.undo.UndoableEdit; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.api.editor.mimelookup.test.MockMimeLookup; +import org.netbeans.junit.NbTestCase; + +/** + * + * @author Miloslav Metelka + */ +public class UndoableEditWrapperTest extends NbTestCase { + + /** Creates a new instance of ZOrderTest */ + public UndoableEditWrapperTest(String name) { + super(name); + } + + public void testWrapping() throws Exception { + MimePath mimePath = MimePath.EMPTY; + MockMimeLookup.setInstances(mimePath, new TestingUndoableEditWrapper()); + BaseDocument bDoc = new BaseDocument(false, ""); + bDoc.addUndoableEditListener(new UndoableEditListener() { + @Override + public void undoableEditHappened(UndoableEditEvent e) { + UndoableEdit edit = e.getEdit(); + assertEquals("Expected WrapCompoundEdit.class", + TestingUndoableEditWrapper.WrapCompoundEdit.class, edit.getClass()); + } + }); + bDoc.insertString(0, "Test", null); + } + +} Index: editor.lib2/manifest.mf --- editor.lib2/manifest.mf Base (BASE) +++ editor.lib2/manifest.mf Locally Modified (Based On LOCAL) @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.modules.editor.lib2/1 -OpenIDE-Module-Implementation-Version: 26 +OpenIDE-Module-Implementation-Version: 27 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/editor/lib2/Bundle.properties OpenIDE-Module-Layer: org/netbeans/modules/editor/lib2/resources/layer.xml OpenIDE-Module-Needs: org.netbeans.modules.editor.actions Index: editor.lib2/nbproject/project.properties --- editor.lib2/nbproject/project.properties Base (BASE) +++ editor.lib2/nbproject/project.properties Locally Modified (Based On LOCAL) @@ -43,7 +43,7 @@ is.autoload=true javac.source=1.6 javac.compilerargs=-Xlint:unchecked -spec.version.base=1.55.0 +spec.version.base=1.56.0 javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml Index: editor.lib2/nbproject/project.xml --- editor.lib2/nbproject/project.xml Base (BASE) +++ editor.lib2/nbproject/project.xml Locally Modified (Based On LOCAL) @@ -188,6 +188,7 @@ org.netbeans.api.editor org.netbeans.spi.editor.codegen + org.netbeans.spi.editor.document org.netbeans.spi.editor.highlighting org.netbeans.spi.editor.highlighting.support org.netbeans.spi.editor.typinghooks Index: editor.lib2/src/org/netbeans/spi/editor/document/UndoableEditWrapper.java --- editor.lib2/src/org/netbeans/spi/editor/document/UndoableEditWrapper.java Base (BASE) +++ editor.lib2/src/org/netbeans/spi/editor/document/UndoableEditWrapper.java Locally New @@ -0,0 +1,68 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.spi.editor.document; + +import javax.swing.text.Document; +import javax.swing.undo.UndoableEdit; +import org.netbeans.api.annotations.common.NonNull; + +/** + * Wrap undoable edits generated by document implementation into a custom undoable edit. + *
+ * Instances should be registered by using @MimeRegistration. + * + * @author Miloslav Metelka + */ +public interface UndoableEditWrapper { + + /** + * Wrap given undoable edit by a custom undoable edit implementation + * (or leave it as it is). + * + * @param edit original undoable edit generated by document (or previous wrapper). + * @param doc document which generated the original undoable edit. + * @return wrap edit or original edit. + * @since 1.56 + */ + @NonNull UndoableEdit wrap(@NonNull UndoableEdit edit, @NonNull Document doc); + +} Index: java.navigation/src/org/netbeans/modules/java/navigation/ClassMemberPanel.java --- java.navigation/src/org/netbeans/modules/java/navigation/ClassMemberPanel.java Base (BASE) +++ java.navigation/src/org/netbeans/modules/java/navigation/ClassMemberPanel.java Locally Modified (Based On LOCAL) @@ -48,9 +48,12 @@ import javax.swing.JComponent; import org.netbeans.api.java.source.ElementHandle; import org.netbeans.spi.navigator.NavigatorPanel; +import org.netbeans.spi.navigator.NavigatorPanelWithUndo; +import org.openide.awt.UndoRedo; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.RequestProcessor; +import org.openide.util.lookup.Lookups; /** * @@ -60,7 +63,7 @@ @NavigatorPanel.Registration(mimeType="text/x-java", position=100, displayName="#LBL_members"), @NavigatorPanel.Registration(mimeType="application/x-class-file", displayName="#LBL_members") }) -public class ClassMemberPanel implements NavigatorPanel { +public class ClassMemberPanel implements NavigatorPanelWithUndo { private ClassMemberPanelUI component; @@ -135,4 +138,9 @@ public static ClassMemberPanel getInstance() { return INSTANCE; } + + @Override + public UndoRedo getUndoRedo() { + return Lookups.forPath("org/netbeans/modules/refactoring").lookup(UndoRedo.class); } +} Index: openide.actions/src/org/openide/actions/Bundle.properties --- openide.actions/src/org/openide/actions/Bundle.properties Base (BASE) +++ openide.actions/src/org/openide/actions/Bundle.properties Locally Modified (Based On LOCAL) @@ -151,3 +151,7 @@ MSG_GC=GC... CTL_GC=Click to force garbage collection +LBL_CannotUndo=Cannot Undo +LBL_CannotRedo=Cannot Redo + + Index: openide.actions/src/org/openide/actions/RedoAction.java --- openide.actions/src/org/openide/actions/RedoAction.java Base (BASE) +++ openide.actions/src/org/openide/actions/RedoAction.java Locally Modified (Based On LOCAL) @@ -43,7 +43,10 @@ */ package org.openide.actions; +import java.awt.HeadlessException; +import java.util.MissingResourceException; import javax.swing.Action; +import javax.swing.JOptionPane; import org.openide.awt.UndoRedo; import org.openide.util.HelpCtx; import org.openide.util.Lookup; @@ -52,16 +55,14 @@ import javax.swing.UIManager; import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; import org.openide.util.ContextAwareAction; import org.openide.util.Exceptions; +import org.openide.windows.TopComponent; +import org.openide.windows.WindowManager; -/** Redo an edit. Since version 6.18 this class -* implements {@link ContextAwareAction}. -* -* @see UndoAction -* @author Ian Formanek, Jaroslav Tulach -*/ + public class RedoAction extends CallableSystemAction implements ContextAwareAction { private static String SWING_DEFAULT_LABEL = UIManager.getString("AbstractUndoableEdit.redoText"); //NOI18N @@ -106,7 +107,7 @@ undoRedo.redo(); } } catch (CannotRedoException ex) { - Exceptions.printStackTrace(ex); + UndoRedoAction.cannotUndoRedo(ex); } UndoAction.updateStatus(); Index: openide.actions/src/org/openide/actions/UndoAction.java --- openide.actions/src/org/openide/actions/UndoAction.java Base (BASE) +++ openide.actions/src/org/openide/actions/UndoAction.java Locally Modified (Based On LOCAL) @@ -56,6 +56,7 @@ import java.beans.*; import java.util.logging.Logger; import java.util.logging.Level; +import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.swing.UIManager; @@ -185,7 +186,7 @@ undoRedo.undo(); } } catch (CannotUndoException ex) { - Exceptions.printStackTrace(ex); + UndoRedoAction.cannotUndoRedo(ex); } updateStatus(); Index: openide.actions/src/org/openide/actions/UndoRedoAction.java --- openide.actions/src/org/openide/actions/UndoRedoAction.java Base (BASE) +++ openide.actions/src/org/openide/actions/UndoRedoAction.java Locally Modified (Based On LOCAL) @@ -41,6 +41,7 @@ package org.openide.actions; import java.awt.EventQueue; +import java.awt.HeadlessException; import java.awt.event.ActionEvent; import javax.swing.Action; import org.openide.awt.UndoRedo; @@ -53,9 +54,11 @@ import java.beans.*; import java.util.Map; +import java.util.MissingResourceException; import java.util.logging.Logger; import java.util.logging.Level; import javax.swing.AbstractAction; +import javax.swing.JOptionPane; import javax.swing.UIManager; import javax.swing.event.*; @@ -221,17 +224,28 @@ undoRedo.undo(); } } catch (CannotUndoException ex) { - Exceptions.printStackTrace(ex); + cannotUndoRedo(ex); } else try { if (undoRedo.canRedo()) { undoRedo.redo(); } } catch (CannotRedoException ex) { - Exceptions.printStackTrace(ex); + cannotUndoRedo(ex); } run(); } + static void cannotUndoRedo(RuntimeException ex) throws MissingResourceException, HeadlessException { + if (ex.getMessage() != null) { + JOptionPane.showMessageDialog( + WindowManager.getDefault().getMainWindow(), + ex.getMessage(), + NbBundle.getMessage(UndoRedoAction.class, ex instanceof CannotUndoException ? "LBL_CannotUndo" : "LBL_CannotRedo"), + JOptionPane.ERROR_MESSAGE); + } + } + + @Override public void propertyChange(PropertyChangeEvent ev) { if (TopComponent.Registry.PROP_ACTIVATED.equals(ev.getPropertyName())) { Index: openide.text/src/org/openide/text/NbDocument.java --- openide.text/src/org/openide/text/NbDocument.java Base (BASE) +++ openide.text/src/org/openide/text/NbDocument.java Locally Modified (Based On LOCAL) @@ -51,6 +51,8 @@ import javax.swing.JToolBar; import javax.swing.SwingUtilities; import javax.swing.text.*; +import javax.swing.undo.UndoableEdit; +import org.openide.awt.UndoRedo; import org.openide.cookies.EditorCookie; @@ -519,6 +521,29 @@ ((Annotatable) doc).removeAnnotation(annotation); } + public static T getEditToBeUndoneOfType(EditorCookie ec, Class type) { + return getEditToBeUndoneRedoneOfType(ec, type, false); + } + + public static T getEditToBeRedoneOfType(EditorCookie ec, Class type) { + return getEditToBeUndoneRedoneOfType(ec, type, true); + } + + private static T getEditToBeUndoneRedoneOfType(EditorCookie ec, Class type, boolean redone) { + UndoRedo ur; + if (ec instanceof CloneableEditorSupport && + ((ur = ((CloneableEditorSupport)ec).getUndoRedo()) instanceof UndoRedoManager)) + { + UndoRedoManager urManager = (UndoRedoManager) ur; + UndoableEdit edit = urManager.editToBeUndoneRedone(redone); + if (type.isInstance(edit)) { + @SuppressWarnings("unchecked") T inst = (T) edit; + return inst; + } + } + return null; + } + /** Specialized version of document that knows how to lock the document * for complex modifications. */ Index: openide.text/src/org/openide/text/UndoRedoManager.java --- openide.text/src/org/openide/text/UndoRedoManager.java Base (BASE) +++ openide.text/src/org/openide/text/UndoRedoManager.java Locally Modified (Based On LOCAL) @@ -564,6 +564,11 @@ undoGroup = null; } + UndoableEdit editToBeUndoneRedone(boolean redone) { // Access for NbDocument + WrapUndoEdit wrapEdit = (WrapUndoEdit) (redone ? editToBeRedone() : editToBeUndone()); + return wrapEdit.delegate(); + } + static String editToString(UndoableEdit edit) { if (edit instanceof WrapUndoEdit) { return toStringTerse(edit) + "->" + toStringTerse(((WrapUndoEdit)edit).delegate()); // NOI18N Index: openide.text/test/unit/src/org/openide/text/EditToBeUndoneRedoneTest.java --- openide.text/test/unit/src/org/openide/text/EditToBeUndoneRedoneTest.java Base (BASE) +++ openide.text/test/unit/src/org/openide/text/EditToBeUndoneRedoneTest.java Locally New @@ -0,0 +1,285 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ + +package org.openide.text; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.VetoableChangeListener; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.Date; +import javax.swing.event.UndoableEditEvent; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.EditorKit; +import javax.swing.text.Position; +import javax.swing.text.StyledDocument; +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoableEdit; +import org.netbeans.junit.NbTestCase; +import org.openide.awt.UndoRedo; +import org.openide.cookies.EditorCookie; +import org.openide.text.CloneableEditorSupport.Env; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; +import org.openide.util.RequestProcessor; +import org.openide.util.RequestProcessor.Task; +import org.openide.windows.CloneableOpenSupport; +import org.openide.windows.CloneableTopComponent; + +/** + * Deadlock of a thread simulating reloading of document and another thread trying to close the file. + * + * @author Miloslav Metelka + */ +public class EditToBeUndoneRedoneTest extends NbTestCase +implements CloneableEditorSupport.Env { + static { + System.setProperty("org.openide.windows.DummyWindowManager.VISIBLE", "false"); + } + /** the support to work with */ + private transient CES support; + + // Env variables + private transient String content = ""; + private transient boolean valid = true; + private transient boolean modified = false; + /** if not null contains message why this document cannot be modified */ + private transient String cannotBeModified; + private transient Date date = new Date (); + private transient final PropertyChangeSupport pcl; + private transient VetoableChangeListener vetoL; + + private transient volatile boolean inReloadBeforeSupportLock; + private transient volatile boolean closing; + + private static EditToBeUndoneRedoneTest RUNNING; + + public EditToBeUndoneRedoneTest(String s) { + super(s); + pcl = new PropertyChangeSupport(this); + } + + protected void setUp () { + support = new CES (this, Lookup.EMPTY); + RUNNING = this; + } + + protected boolean runInEQ() { + return false; + } + + @Override + protected int timeOut() { + return 15000; + } + + private Object writeReplace () { + return new Replace (); + } + + public void testUndoRedoEdits() throws Exception { + Document doc = support.openDocument(); + doc.insertString(0, "a", null); + UndoRedo.Manager ur = support.getUndoRedo(); + MyEdit myEdit = NbDocument.getEditToBeUndoneOfType(support, MyEdit.class); + assertNotNull("Expected valid myEdit", myEdit); + ur.undo(); + myEdit = NbDocument.getEditToBeRedoneOfType(support, MyEdit.class); + assertNotNull("Expected valid myEdit", myEdit); + } + + // + // Implementation of the CloneableEditorSupport.Env + // + + public synchronized void addPropertyChangeListener(PropertyChangeListener l) { + pcl.addPropertyChangeListener(l); + } + public synchronized void removePropertyChangeListener(PropertyChangeListener l) { + pcl.removePropertyChangeListener(l); + } + + public synchronized void addVetoableChangeListener(VetoableChangeListener l) { + assertNull ("This is the first veto listener", vetoL); + vetoL = l; + } + public void removeVetoableChangeListener(VetoableChangeListener l) { + assertEquals ("Removing the right veto one", vetoL, l); + vetoL = null; + } + + public CloneableOpenSupport findCloneableOpenSupport() { + return RUNNING.support; + } + + public String getMimeType() { + return "text/plain"; + } + + public Date getTime() { + return date; + } + + public InputStream inputStream() throws IOException { + return new ByteArrayInputStream (content.getBytes ()); + } + public OutputStream outputStream() throws IOException { + class ContentStream extends ByteArrayOutputStream { + public void close () throws IOException { + super.close (); + content = new String (toByteArray ()); + } + } + + return new ContentStream (); + } + + public boolean isValid() { + return valid; + } + + public boolean isModified() { + return modified; + } + + public void markModified() throws IOException { + if (cannotBeModified != null) { + final String notify = cannotBeModified; + IOException e = new IOException () { + public String getLocalizedMessage () { + return notify; + } + }; + Exceptions.attachLocalizedMessage(e, cannotBeModified); + throw e; + } + + modified = true; + } + + public void unmarkModified() { + modified = false; + } + + /** Implementation of the CES */ + private static final class CES extends CloneableEditorSupport implements EditorCookie { + public CES (Env env, Lookup l) { + super (env, l); + } + + @Override + protected EditorKit createEditorKit () { + // Important to use NbLikeEditorKit since otherwise FilterDocument + // would be created with improper runAtomic() + return new MyKit (); + } + public CloneableTopComponent.Ref getRef () { + return allEditors; + } + + protected String messageName() { + return "Name"; + } + + protected String messageOpened() { + return "Opened"; + } + + protected String messageOpening() { + return "Opening"; + } + + protected String messageSave() { + return "Save"; + } + + protected String messageToolTip() { + return "ToolTip"; + } + + } + + private static final class Replace implements Serializable { + public Object readResolve () { + return RUNNING; + } + } + + private static final class MyEdit extends CompoundEdit { // Marker custom undo edit + + } + + private static final class MyKit extends NbLikeEditorKit { + + @Override + public Document createDefaultDocument() { + return new Doc() { + + @Override + protected void fireUndoableEditUpdate(UndoableEditEvent e) { + UndoableEdit edit = e.getEdit(); + MyEdit wrapEdit = new MyEdit(); + wrapEdit.addEdit(edit); + wrapEdit.end(); + e = new UndoableEditEvent(e.getSource(), wrapEdit); + + super.fireUndoableEditUpdate(e); + } + + }; + } + + + } +} Index: projectui/src/org/netbeans/modules/project/ui/ProjectTab.java --- projectui/src/org/netbeans/modules/project/ui/ProjectTab.java Base (BASE) +++ projectui/src/org/netbeans/modules/project/ui/ProjectTab.java Locally Modified (Based On LOCAL) @@ -101,6 +101,7 @@ import org.openide.awt.ActionReferences; import org.openide.awt.ActionRegistration; import org.openide.awt.StatusDisplayer; +import org.openide.awt.UndoRedo; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.explorer.view.BeanTreeView; @@ -125,6 +126,7 @@ import org.openide.util.RequestProcessor.Task; import org.openide.util.Utilities; import org.openide.util.WeakListeners; +import org.openide.util.lookup.Lookups; import org.openide.windows.TopComponent; import org.openide.windows.WindowManager; @@ -135,7 +137,7 @@ * @author Petr Hrebejk */ public class ProjectTab extends TopComponent - implements ExplorerManager.Provider, PropertyChangeListener { + implements ExplorerManager.Provider, PropertyChangeListener, UndoRedo.Provider { public static final String ID_LOGICAL = "projectTabLogical_tc"; // NOI18N public static final String ID_PHYSICAL = "projectTab_tc"; // NOI18N @@ -248,6 +250,11 @@ return manager; } + @Override + public UndoRedo getUndoRedo() { + return Lookups.forPath("org/netbeans/modules/refactoring").lookup(UndoRedo.class); + } + /* Singleton accessor. As ProjectTab is persistent singleton this * accessor makes sure that ProjectTab is deserialized by window system. * Uses known unique TopComponent ID TC_ID = "projectTab_tc" to get ProjectTab instance Index: refactoring.api/apichanges.xml --- refactoring.api/apichanges.xml Base (BASE) +++ refactoring.api/apichanges.xml Locally Modified (Based On LOCAL) @@ -49,6 +49,22 @@ Refactoring API + + + Added RefactoringCommit and ModificationResult SPI classes. + + + + + +

+ Added RefactoringCommit and ModificationResult SPI classes. +

+
+ + + +
Added CopyRefactoring to support Copy of multiple files. Index: refactoring.api/nbproject/project.xml --- refactoring.api/nbproject/project.xml Base (BASE) +++ refactoring.api/nbproject/project.xml Locally Modified (Based On LOCAL) @@ -46,10 +46,19 @@ 3 - 3.1 + 3.20.1 + org.netbeans.modules.editor.lib2 + + + + 1 + 1.56 + + + org.netbeans.modules.editor.mimelookup @@ -59,6 +68,15 @@ + org.netbeans.modules.parsing.api + + + + 1 + 1.51 + + + org.netbeans.modules.projectapi @@ -175,15 +193,6 @@ 6.6 - - org.netbeans.modules.parsing.api - - - - 1 - 1.51 - - Index: refactoring.api/src/org/netbeans/modules/refactoring/api/RefactoringSession.java --- refactoring.api/src/org/netbeans/modules/refactoring/api/RefactoringSession.java Base (BASE) +++ refactoring.api/src/org/netbeans/modules/refactoring/api/RefactoringSession.java Locally Modified (Based On LOCAL) @@ -53,15 +53,25 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.text.Document; import org.netbeans.api.annotations.common.CheckForNull; import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.editor.BaseDocument; import org.netbeans.modules.refactoring.api.impl.ProgressSupport; import org.netbeans.modules.refactoring.api.impl.SPIAccessor; import org.netbeans.modules.refactoring.spi.RefactoringElementImplementation; import org.netbeans.modules.refactoring.spi.RefactoringElementsBag; +import org.netbeans.modules.refactoring.spi.RefactoringCommit; import org.netbeans.modules.refactoring.spi.Transaction; import org.netbeans.modules.refactoring.spi.impl.UndoManager; +import org.netbeans.modules.refactoring.spi.impl.UndoableWrapper; +import org.netbeans.spi.editor.document.UndoableEditWrapper; import org.openide.LifecycleManager; +import org.openide.cookies.EditorCookie; +import org.openide.filesystems.FileObject; +import org.openide.loaders.DataObject; +import org.openide.loaders.DataObjectNotFoundException; import org.openide.util.Exceptions; import org.openide.util.Parameters; @@ -128,9 +138,13 @@ } } } finally { + UndoableWrapper wrapper = MimeLookup.getLookup("").lookup(UndoableWrapper.class); for (Transaction commit:SPIAccessor.DEFAULT.getCommits(bag)) { + setWrappers(commit, wrapper); commit.commit(); + unsetWrappers(commit, wrapper); } + wrapper.close(); } if (saveAfterDone) { LifecycleManager.getDefault().saveAll(); @@ -175,10 +189,14 @@ f.undoChange(); } } + UndoableWrapper wrapper = MimeLookup.getLookup("").lookup(UndoableWrapper.class); for (ListIterator commitIterator = commits.listIterator(commits.size()); commitIterator.hasPrevious();) { - commitIterator.previous().rollback(); + final Transaction commit = commitIterator.previous(); + setWrappers(commit, wrapper); + commit.rollback(); + unsetWrappers(commit, wrapper); } - + wrapper.close(); while (it.hasPrevious()) { fireProgressListenerStep(); RefactoringElementImplementation element = (RefactoringElementImplementation) it.previous(); @@ -254,6 +272,37 @@ } } + private void setWrappers(Transaction commit, UndoableWrapper wrap) { + wrap.setActive(true); + + // if (!(commit instanceof RefactoringCommit)) + // return; + // for (FileObject f:((RefactoringCommit) commit).getModifiedFiles()) { + // Document doc = getDocument(f); + // if (doc!=null) + // doc.putProperty(BaseDocument.UndoableEditWrapper.class, wrap); + // } + } + + private void unsetWrappers(Transaction commit, UndoableWrapper wrap) { + wrap.setActive(false); + + // setWrappers(commit, null); + } + + private Document getDocument(FileObject f) { + try { + DataObject dob = DataObject.find(f); + EditorCookie cookie = dob.getLookup().lookup(EditorCookie.class); + if (cookie == null) + return null; + return cookie.getDocument(); + } catch (DataObjectNotFoundException ex) { + return null; + } + } + + private class ElementsCollection extends AbstractCollection { @Override public Iterator iterator() { Index: refactoring.api/src/org/netbeans/modules/refactoring/api/resources/layer.xml --- refactoring.api/src/org/netbeans/modules/refactoring/api/resources/layer.xml Base (BASE) +++ refactoring.api/src/org/netbeans/modules/refactoring/api/resources/layer.xml Locally Modified (Based On LOCAL) @@ -54,11 +54,6 @@ - - - - - @@ -82,6 +77,8 @@ + + Index: refactoring.api/src/org/netbeans/modules/refactoring/spi/BackupFacility.java --- refactoring.api/src/org/netbeans/modules/refactoring/spi/BackupFacility.java Base (BASE) +++ refactoring.api/src/org/netbeans/modules/refactoring/spi/BackupFacility.java Locally Modified (Based On LOCAL) @@ -96,7 +96,9 @@ * @see RefactoringElementsBag#addFileChange * @see BackupFacility.Handle * @author Jan Becicka + * @deprecated */ +@Deprecated public abstract class BackupFacility { private BackupFacility() { Index: refactoring.api/src/org/netbeans/modules/refactoring/spi/BackupFacility2.java --- refactoring.api/src/org/netbeans/modules/refactoring/spi/BackupFacility2.java Base (BackupFacility.java) +++ refactoring.api/src/org/netbeans/modules/refactoring/spi/BackupFacility2.java Locally Copied @@ -41,36 +41,37 @@ * Version 2 license, then the option applies only if the new code is * made subject to such option by the copyright holder. */ - package org.netbeans.modules.refactoring.spi; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; +import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; +import java.util.logging.Logger; +import org.netbeans.editor.BaseDocument; +import org.netbeans.modules.refactoring.spi.impl.UndoableWrapper; +import org.netbeans.modules.refactoring.spi.impl.UndoableWrapper.UndoableEditDelegate; +import org.openide.cookies.EditorCookie; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; +import org.openide.loaders.DataObject; +import org.openide.loaders.DataObjectNotFoundException; +import org.openide.text.CloneableEditorSupport; +import org.openide.text.NbDocument; +import org.openide.util.Exceptions; import org.openide.util.Lookup; +import sun.misc.IOUtils; /** - * Simple backup facility - * can be used to backup files and implement undo - * For instance Java Refactoring module implements undo this way: + * Simple backup facility can be used to backup files and implement undo For + * instance Java Refactoring module implements undo this way: * - * public Problem prepare(RefactoringElementsBag elements) { - * . - * . - * elements.registerTransaction(new RetoucheCommit(results)); - * } + * public Problem prepare(RefactoringElementsBag elements) { . . + * elements.registerTransaction(new RetoucheCommit(results)); } * * where RetoucheCommit is Transaction: *
@@ -89,6 +90,7 @@
  * 
* * You can register your own implementation via META-INF services. + * * @see Transaction * @see RefactoringElementImplementation#performChange * @see RefactoringElementImplementation#undoChange @@ -97,15 +99,17 @@ * @see BackupFacility.Handle * @author Jan Becicka */ -public abstract class BackupFacility { +abstract class BackupFacility2 { - private BackupFacility() { + private static final Logger LOG = Logger.getLogger("org.netbeans.modules.refactoring.Undo"); + + private BackupFacility2() { } + private static BackupFacility2 defaultInstance; - private static BackupFacility defaultInstance; - /** - * does beckup + * does backup + * * @param file file(s) to backup * @return handle which can be used to restore files * @throws java.io.IOException if backup failed @@ -114,6 +118,7 @@ /** * does backup + * * @param fileObjects FileObjects to backup * @return handle which can be used to restore files * @throws java.io.IOException @@ -123,10 +128,8 @@ } /** - * do cleanup - * all backup files are deleted - * all internal structures cleared - * default implementa + * do cleanup all backup files are deleted all internal structures cleared + * default implemntation */ public abstract void clear(); @@ -135,12 +138,12 @@ * class in META-INF services -> this class is returned. Otherwise default * implementation is used. */ - public static BackupFacility getDefault() { - BackupFacility instance = Lookup.getDefault().lookup(BackupFacility.class); + public static BackupFacility2 getDefault() { + BackupFacility2 instance = Lookup.getDefault().lookup(BackupFacility2.class); return (instance != null) ? instance : getDefaultInstance(); } - private static synchronized BackupFacility getDefaultInstance() { + private static synchronized BackupFacility2 getDefaultInstance() { if (defaultInstance == null) { defaultInstance = new DefaultImpl(); } @@ -149,57 +152,188 @@ } /** - * Handle class representing handle to file(s), which were backuped - * by - * {@link org.netbeans.modules.refactoring.spi.BackupFacility#backup()} + * Handle class representing handle to file(s), which were backuped by {@link org.netbeans.modules.refactoring.spi.BackupFacility#backup()} */ public interface Handle { + /** - * restore file(s), which was stored by {@link org.netbeans.modules.refactoring.spi.BackupFacility#backup()} + * restore file(s), which was stored by {@link org.netbeans.modules.refactoring.spi.BackupFacility#backup()} + * * @throws java.io.IOException if restore failed. */ - void restore() throws IOException; + public abstract void restore() throws java.io.IOException; + + void storeChecksum() throws IOException; + + public Collection checkChecksum(boolean undo) throws IOException; } private static class DefaultHandle implements Handle { - List handle; - DefaultImpl instance; + + private List handle; + private DefaultImpl instance; + private DefaultHandle(DefaultImpl instance, List handles) { this.handle = handles; this.instance = instance; } + @Override public void restore() throws IOException { - for (long l:handle) { + for (long l : handle) { instance.restore(l); } } + + @Override + public void storeChecksum() throws IOException { + for (long l : handle) { + instance.storeChecksum(l); } + } - private static class DefaultImpl extends BackupFacility { + @Override + public Collection checkChecksum(boolean undo) throws IOException { + Collection result = new LinkedList(); + for (long l : handle) { + String checkChecksum = instance.checkChecksum(l, undo); + if (checkChecksum !=null) { + result.add(checkChecksum); + } + } + return result; + } + } + + private static class DefaultImpl extends BackupFacility2 { + private long currentId = 0; private Map map = new HashMap(); + private String MD5toString(byte[] digest) { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < digest.length; i++) { + b.append(Integer.toHexString(0xFF & digest[i])); + } + return b.toString(); + } + + private void storeChecksum(long l) throws IOException { + BackupEntry backup = map.get(l); + File f = new File(backup.path); + FileObject fo = FileUtil.toFileObject(f); + DataObject dob = DataObject.find(fo); + if (dob != null) { + CloneableEditorSupport ces = dob.getLookup().lookup(CloneableEditorSupport.class); + final BaseDocument doc = (BaseDocument) ces.getDocument(); + if (doc !=null && doc.isAtomicLock()) { + //workaround to avoid deadlock + return; + } + } + LOG.fine("Storing MD5 for " + backup.path); + backup.checkSum = getMD5(getInputStream(backup.path)); + LOG.fine("MD5 is: " + MD5toString(backup.checkSum)); + } + + private String checkChecksum(long l, boolean undo) { + + try { + BackupEntry backup = map.get(l); + File f = new File(backup.path); + FileObject fo = FileUtil.toFileObject(f); + DataObject dob = DataObject.find(fo); + if (dob != null) { + CloneableEditorSupport ces = dob.getLookup().lookup(CloneableEditorSupport.class); + + final BaseDocument doc = (BaseDocument) ces.getDocument(); + if (doc != null && doc.isAtomicLock()) { + //workaround to avoid deadlock + return null; + } else { + EditorCookie editor = dob.getLookup().lookup(EditorCookie.class); + if (editor != null && doc!=null && editor.isModified()) { + UndoableEditDelegate edit = undo?NbDocument.getEditToBeUndoneOfType(editor, UndoableWrapper.UndoableEditDelegate.class):NbDocument.getEditToBeRedoneOfType(editor, UndoableWrapper.UndoableEditDelegate.class); + if (edit == null) { + try { + LOG.fine("Editor Undo Different"); + return backup.path.toURL().getPath(); + } catch (MalformedURLException ex) { + Exceptions.printStackTrace(ex); + } + } + } + + } + } + + try { + LOG.fine("Checking MD5 for " + backup.path); + byte[] ts = getMD5(getInputStream(backup.path)); + if (!Arrays.equals(backup.checkSum, ts)) { + LOG.fine("MD5 check failed"); + return backup.path.toURL().getPath(); + } + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } catch (DataObjectNotFoundException ex) { + Exceptions.printStackTrace(ex); + } + return null; + } + + private InputStream getInputStream(URI path) throws IOException { + File f = new File(path); + FileObject fo = FileUtil.toFileObject(f); + DataObject dob = DataObject.find(fo); + CloneableEditorSupport ces = dob.getLookup().lookup(CloneableEditorSupport.class); + if (ces != null && ces.isModified()) { + LOG.fine("Editor Input Stream"); + return ces.getInputStream(); + } + LOG.fine("File Input Stream"); + return fo.getInputStream(); + } + private class BackupEntry { + private File file; private URI path; + private byte[] checkSum; + private boolean undo = true; + + public BackupEntry() { } - /** Creates a new instance of BackupFacility */ + public boolean isUndo() { + return undo; + } + + public void setUndo(boolean undo) { + this.undo = undo; + } + } + + /** + * Creates a new instance of BackupFacility + */ private DefaultImpl() { } @Override - public Handle backup(FileObject ... file) throws IOException { + public Handle backup(FileObject... file) throws IOException { ArrayList list = new ArrayList(); - for (FileObject f:file) { + for (FileObject f : file) { list.add(backup(f)); } return new DefaultHandle(this, list); } + /** - * does beckup + * does backup + * * @param file to backup * @return id of backup file * @throws java.io.IOException if backup failed @@ -217,14 +351,45 @@ throw (IOException) new IOException(file.toString()).initCause(ex); } } + + private byte[] getMD5(InputStream is) throws IOException { + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + try { + is = new DigestInputStream(is, md); + IOUtils.readFully(is, -1, true); + } finally { + is.close(); + } + return md.digest(); + } catch (NoSuchAlgorithmException ex) { + throw new IOException(ex); + } + } + + private static java.lang.reflect.Field undoRedo; + + static { + try { + //obviously hack. See 108616 and 48427 + undoRedo = org.openide.text.CloneableEditorSupport.class.getDeclaredField("undoRedo"); //NOI18N + undoRedo.setAccessible(true); + } catch (NoSuchFieldException ex) { + Exceptions.printStackTrace(ex); + } catch (SecurityException ex) { + Exceptions.printStackTrace(ex); + } + } + /** * restore file, which was stored by backup(file) + * * @param id identification of backup transaction * @throws java.io.IOException if restore failed. */ void restore(long id) throws IOException { BackupEntry entry = map.get(id); - if(entry==null) { + if (entry == null) { throw new IllegalArgumentException("Backup with id " + id + "does not exist"); // NOI18N } File backup = File.createTempFile("nbbackup", null); //NOI18N @@ -232,10 +397,13 @@ File f = new File(entry.path); if (createNewFile(f)) { backup.createNewFile(); - copy(f,backup); + copy(f, backup); } FileObject fileObj = FileUtil.toFileObject(f); - copy(entry.file,fileObj); + + if (!tryUndoOrRedo(fileObj, entry)) { + copy(entry.file, fileObj); + } entry.file.delete(); if (backup.exists()) { entry.file = backup; @@ -244,14 +412,62 @@ } } + private boolean tryUndoOrRedo(final FileObject fileObj, final BackupEntry entry) throws DataObjectNotFoundException { + DataObject dob = DataObject.find(fileObj); + if (dob != null) { + CloneableEditorSupport ces = dob.getLookup().lookup(CloneableEditorSupport.class); + final org.openide.awt.UndoRedo.Manager manager; + try { + manager = (org.openide.awt.UndoRedo.Manager) undoRedo.get(ces); + final BaseDocument doc = (BaseDocument) ces.getDocument(); + if (doc==null) { + return false; + } + if (doc.isAtomicLock()) { + //undo already performed + if (entry.isUndo()) { + entry.setUndo(false); + } else { + entry.setUndo(true); + } + } else { + if ((entry.isUndo() && manager.canUndo()) || (!entry.isUndo() && manager.canRedo())) { + doc.runAtomic(new Runnable() { + + @Override + public void run() { + if (entry.isUndo()) { + manager.undo(); + entry.setUndo(false); + } else { + manager.redo(); + entry.setUndo(true); + } + } + }); + } else { + return false; + } + } + return true; + } catch (IllegalArgumentException ex) { + Exceptions.printStackTrace(ex); + } catch (IllegalAccessException ex) { + Exceptions.printStackTrace(ex); + } + } + return false; + } + /** * workaround for #93390 */ private boolean createNewFile(File f) throws IOException { - if (f.exists()) + if (f.exists()) { return true; + } File parent = f.getParentFile(); - if (parent!=null) { + if (parent != null) { createNewFolder(parent); } FileUtil.createData(f); @@ -297,7 +513,7 @@ @Override public void clear() { - for(BackupEntry entry: map.values()) { + for (BackupEntry entry : map.values()) { entry.file.delete(); } map.clear(); Index: refactoring.api/src/org/netbeans/modules/refactoring/spi/ModificationResult.java --- refactoring.api/src/org/netbeans/modules/refactoring/spi/ModificationResult.java Base (BASE) +++ refactoring.api/src/org/netbeans/modules/refactoring/spi/ModificationResult.java Locally New @@ -0,0 +1,84 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.refactoring.spi; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import org.openide.filesystems.FileObject; + +/** + * Result of changes performed by refactoring plugin. + * @author Jan Becicka + * @since 1.23 + */ +public interface ModificationResult { + + /** + * New source text for given file. + * + * @param file + * @return new source text + * @throws IOException + * @throws IllegalArgumentException + */ + public String getResultingSource(FileObject file) throws IOException, IllegalArgumentException; + + /** + * FileObjects modified by this ModificationResult. + * @return + */ + public Collection getModifiedFileObjects(); + + /** + * New files generated by this ModificationResult. + * @return + */ + public Collection getNewFiles(); + + /** + * Performs commit of changes into files. + * @throws IOException + */ + public void commit() throws IOException; + +} Index: refactoring.api/src/org/netbeans/modules/refactoring/spi/RefactoringCommit.java --- refactoring.api/src/org/netbeans/modules/refactoring/spi/RefactoringCommit.java Base (RetoucheCommit.java) +++ refactoring.api/src/org/netbeans/modules/refactoring/spi/RefactoringCommit.java Locally Renamed @@ -41,15 +41,22 @@ * Version 2 license, then the option applies only if the new code is * made subject to such option by the copyright holder. */ -package org.netbeans.modules.refactoring.java.plugins; +package org.netbeans.modules.refactoring.spi; import java.io.File; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import org.netbeans.api.java.source.ModificationResult; -import org.netbeans.modules.refactoring.spi.BackupFacility; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.modules.refactoring.spi.BackupFacility2; +import org.netbeans.modules.refactoring.spi.BackupFacility2.Handle; import org.netbeans.modules.refactoring.spi.Transaction; import org.openide.cookies.EditorCookie; import org.openide.filesystems.FileObject; @@ -59,45 +66,125 @@ import org.openide.util.Exceptions; /** - * + * Default implementation of {@link Transaction} * @author Jan Becicka + * @since 1.23 */ -public class RetoucheCommit implements Transaction { - private static final Logger LOG = Logger.getLogger(RetoucheCommit.class.getName()); - List ids = new ArrayList(); + public final class RefactoringCommit implements Transaction { + + private static final Logger LOG = Logger.getLogger(RefactoringCommit.class.getName()); + + /** + * FileObjects modified by this Transaction + * + * @return collection of FileObjects + */ + @NonNull Collection getModifiedFiles() { + ArrayList result = new ArrayList(); + for (ModificationResult modification:results) { + result.addAll(modification.getModifiedFileObjects()); + } + return result; + } + + private static class CannotUndoRefactoring extends CannotUndoException { + + private Collection files; + + private CannotUndoRefactoring(Collection checkChecksum) { + super(); + this.files = checkChecksum; + } + + @Override + public String getMessage() { + StringBuilder b = new StringBuilder("Cannot Undo.\nFollowing files were modified:\n"); + for (String f:files) { + b.append(f); + b.append('\n'); + } + return b.toString(); + } + + + + public Collection getFiles() { + return files; + } + } + + private static class CannotRedoRefactoring extends CannotRedoException { + + private Collection files; + + private CannotRedoRefactoring(Collection checkChecksum) { + this.files = checkChecksum; + } + + public Collection getFiles() { + return files; + } + + @Override + public String getMessage() { + StringBuilder b = new StringBuilder("Cannot Redo.\nFollowing files were modified:\n"); + for (String f:files) { + b.append(f); + b.append('\n'); + } + return b.toString(); + } + + } + + + List ids = new ArrayList(); private boolean commited = false; - Collection results; + Collection results; private Set newFiles; - public RetoucheCommit(Collection results) { + /** + * RefactoringCommit is just collection of ModificationResults + * @param results + */ + public RefactoringCommit(Collection results) { this.results = results; } - @Override public void commit() { try { if (commited) { - for (BackupFacility.Handle id:ids) { + for (BackupFacility2.Handle id : ids) { + Collection checkChecksum = id.checkChecksum(false); + if (!checkChecksum.isEmpty()) { + throw new CannotRedoRefactoring(checkChecksum); + } + } + + for (BackupFacility2.Handle id:ids) { try { id.restore(); + id.storeChecksum(); } catch (IOException ex) { throw new RuntimeException(ex); } } } else { commited = true; - for (ModificationResult result:results) { - ids.add(BackupFacility.getDefault().backup(result.getModifiedFileObjects())); + for (ModificationResult result : results) { + Handle backupid = BackupFacility2.getDefault().backup(result.getModifiedFileObjects()); + ids.add(backupid); if (newFiles == null) { newFiles = new HashSet(); } newFiles.addAll(result.getNewFiles()); result.commit(); - } + backupid.storeChecksum(); openNewFiles(newFiles); } + } } catch (IOException ex) { throw new RuntimeException(ex); @@ -105,22 +192,29 @@ } private boolean newFilesStored = false; - @Override + public void rollback() { - for (BackupFacility.Handle id:ids) { try { + for (BackupFacility2.Handle id : ids) { + Collection checkChecksum = id.checkChecksum(true); + if (!checkChecksum.isEmpty()) { + throw new CannotUndoRefactoring(checkChecksum); + } + + try { id.restore(); + id.storeChecksum(); } catch (IOException ex) { throw new RuntimeException(ex); } } boolean localStored = false; - if (newFiles!=null) { - for (File f:newFiles) { + if (newFiles != null) { + for (File f : newFiles) { try { FileObject fo = FileUtil.toFileObject(f); if (!newFilesStored) { - ids.add(BackupFacility.getDefault().backup(fo)); + ids.add(BackupFacility2.getDefault().backup(fo)); localStored = true; } fo.delete(); @@ -130,8 +224,12 @@ } newFilesStored |= localStored; } + } catch (IOException ex) { + Exceptions.printStackTrace(ex); } + } + private static void openNewFiles(Set newFiles) { if (newFiles == null) { return; Index: refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/Bundle.properties --- refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/Bundle.properties Base (BASE) +++ refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/Bundle.properties Locally Modified (Based On LOCAL) @@ -51,6 +51,12 @@ LBL_MoveAction=&Move... LBL_Undo=U&ndo LBL_Redo=Red&o +MSG_ReallyUndo=Really undo {0} +MSG_ConfirmUndo=Confirm undo +MSG_ReallyRedo=Really redo {0} +MSG_ConfirmRedo=Confirm redo +MSG_ConfirmRefresh=Do you want to refresh refactoring? +MSG_FileModified=Some files were modified #Refactoring Preview Window LBL_CloseWindow=Close Tab Index: refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/InvalidationListener.java --- refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/InvalidationListener.java Base (BASE) +++ refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/InvalidationListener.java Locally Deleted @@ -1,55 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * Oracle and Java are registered trademarks of Oracle and/or its affiliates. - * Other names may be trademarks of their respective owners. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, the - * "License"). You may not use this file except in compliance with the - * License. You can obtain a copy of the License at - * http://www.netbeans.org/cddl-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ - -package org.netbeans.modules.refactoring.spi.impl; - -import java.util.EventListener; - -/** - * - * @author Jan Becicka - */ -public interface InvalidationListener extends EventListener { - void invalidateObject(); -} Index: refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/ParametersPanel.java --- refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/ParametersPanel.java Base (BASE) +++ refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/ParametersPanel.java Locally Modified (Based On LOCAL) @@ -86,7 +86,7 @@ * * @author Martin Matula, Jan Becicka */ -public class ParametersPanel extends JPanel implements ProgressListener, ChangeListener, InvalidationListener { +public class ParametersPanel extends JPanel implements ProgressListener, ChangeListener { public static final String JUMP_TO_FIRST_OCCURENCE = "JUMP_TO_FIRST_OCCURENCE"; //NOI18N @@ -478,10 +478,10 @@ null); DialogDisplayer.getDefault().notifyLater(nd); } else { - UndoWatcher.watch(session, ParametersPanel.this); + //UndoWatcher.watch(session, ParametersPanel.this); session.addProgressListener(ParametersPanel.this); session.doRefactoring(true); - UndoWatcher.stopWatching(ParametersPanel.this); + //UndoWatcher.stopWatching(ParametersPanel.this); } } } finally { @@ -1031,10 +1031,6 @@ return rui.getHelpCtx(); } - @Override - public void invalidateObject() { - } - private RefactoringSession getResult() { synchronized (RESULT_LOCK) { return result; Index: refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/RedoAction.java --- refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/RedoAction.java Base (BASE) +++ refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/RedoAction.java Locally Deleted @@ -1,126 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * Oracle and Java are registered trademarks of Oracle and/or its affiliates. - * Other names may be trademarks of their respective owners. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, the - * "License"). You may not use this file except in compliance with the - * License. You can obtain a copy of the License at - * http://www.netbeans.org/cddl-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ - -package org.netbeans.modules.refactoring.spi.impl; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import javax.swing.Action; -import javax.swing.SwingUtilities; -import org.openide.awt.ActionID; -import org.openide.awt.ActionReference; -import org.openide.awt.ActionRegistration; -import org.openide.util.HelpCtx; -import org.openide.util.NbBundle; -import org.openide.util.actions.CallableSystemAction; - -@ActionID(id = "org.netbeans.modules.refactoring.spi.impl.RedoAction", category = "Refactoring") -@ActionRegistration(displayName = "#LBL_Redo") -@ActionReference(path = "Menu/Refactoring" , name = "RedoAction", position = 2100) -public class RedoAction extends CallableSystemAction implements PropertyChangeListener { - - private UndoManager undoManager; - - public RedoAction() { - putValue(Action.NAME, getString("LBL_Redo")); //NOI18N - putValue("noIconInMenu", Boolean.TRUE); // NOI18N - undoManager = UndoManager.getDefault(); - undoManager.addPropertyChangeListener(this); - updateState(); - } - - @Override - public void propertyChange (PropertyChangeEvent event) { - updateState(); - } - - private void updateState() { - String desc = undoManager.getRedoDescription(); - String name = getString("LBL_Redo"); - if (desc != null) { - name += " [" + desc + "]"; //NOI18N - } - - final String n = name; - final boolean b = undoManager.isRedoAvailable(); - Runnable r = new Runnable() { - @Override - public void run() { - setEnabled(b); - putValue(Action.NAME, n); - } - }; - - if (SwingUtilities.isEventDispatchThread()) { - r.run(); - } else { - SwingUtilities.invokeLater(r); - } - } - - private static final String getString(String key) { - return NbBundle.getMessage(RedoAction.class, key); - } - - @Override - public void performAction() { - undoManager.redo(); - undoManager.saveAll(); - } - - @Override - public org.openide.util.HelpCtx getHelpCtx() { - return HelpCtx.DEFAULT_HELP; - } - - @Override - public String getName() { - return (String) getValue(Action.NAME); - } - - @Override - protected boolean asynchronous() { - return true; - } -} Index: refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/RefactoringPanel.java --- refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/RefactoringPanel.java Base (BASE) +++ refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/RefactoringPanel.java Locally Modified (Based On LOCAL) @@ -58,6 +58,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.Collection; +import java.util.Map.Entry; import java.util.prefs.Preferences; import javax.swing.*; import javax.swing.border.EmptyBorder; @@ -81,6 +82,8 @@ import org.openide.DialogDisplayer; import org.openide.ErrorManager; import org.openide.awt.Mnemonics; +import org.openide.filesystems.FileObject; +import org.openide.loaders.DataObject; import org.openide.text.CloneableEditorSupport; import org.openide.text.PositionBounds; import org.openide.util.*; @@ -91,7 +94,7 @@ * * @author Pavel Flaska, Martin Matula */ -public class RefactoringPanel extends JPanel implements InvalidationListener { +public class RefactoringPanel extends JPanel { private static final RequestProcessor RP = new RequestProcessor(RefactoringPanel.class.getName(), 1, false, false); // PRIVATE FIELDS @@ -502,6 +505,18 @@ */ private void refactor() { checkEventThread(); + if (!checkTimeStamps()) { + if (JOptionPane.showConfirmDialog( + this, + NbBundle.getMessage(RefactoringPanel.class, "MSG_ConfirmRefresh"), + NbBundle.getMessage(RefactoringPanel.class, "MSG_FileModified"), + JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { + refresh(true); + return; + } else { + return; + } + } disableComponents(RefactoringPanel.this); progressListener = new ProgressL(); RP.post(new Runnable() { @@ -603,7 +618,10 @@ requestFocus(); } - @Override + /** + * TODO: probably useless + * remove + */ public void invalidateObject() { if (isQuery) { return; @@ -681,7 +699,7 @@ RP.post(new Runnable() { @Override public void run() { - Set editorSupports = new HashSet(); + Set fileObjects = new HashSet(); int errorsNum = 0; if (!isQuery) { for (Iterator iter = elements.iterator(); iter.hasNext(); ) { @@ -748,10 +766,7 @@ } } PositionBounds pb = e.getPosition(); - if (pb != null) { - CloneableEditorSupport ces = pb.getBegin().getCloneableEditorSupport(); - editorSupports.add(ces); - } + fileObjects.add(e.getParentFile()); if (i % 10 == 0) progressHandle.progress(i/10); @@ -760,7 +775,8 @@ //[retouche] JavaModel.getJavaRepository().endTrans(); } - UndoManager.getDefault().watch(editorSupports, RefactoringPanel.this); + //UndoManager.getDefault().watch(editorSupports, RefactoringPanel.this); + storeTimeStamps(fileObjects); } catch (RuntimeException t) { cleanupTreeElements(); throw t; @@ -857,6 +873,46 @@ }); } + private Map timeStamps = new HashMap(); + + private void storeTimeStamps(Set fileObjects) { + timeStamps.clear(); + for (FileObject fo:fileObjects) { + timeStamps.put(fo, fo.lastModified().getTime()); + } + } + + /** + * @return true if timestamps are OK + */ + private boolean checkTimeStamps() { + Set modified = getModifiedFileObjects(); + for (Entry entry: timeStamps.entrySet()) { + if (modified.contains(entry.getKey())) + return false; + if (!entry.getKey().isValid()) + return false; + if (entry.getKey().lastModified().getTime() != entry.getValue()) + return false; + } + return true; + } + + private Set getModifiedFileObjects() { + Set result = new HashSet(); + for (DataObject dob: DataObject.getRegistry().getModified()) { + result.add(dob.getPrimaryFile()); + } + return result; + } + + private void clearTimeStamps() { + timeStamps.clear(); + } + + + + private void createTree(TreeNode root) throws MissingResourceException { if (tree == null) { // add panel with appropriate content @@ -1069,7 +1125,8 @@ } */ protected void closeNotify() { - UndoWatcher.stopWatching(this); + clearTimeStamps(); + //UndoWatcher.stopWatching(this); if (tree!=null) { ToolTipManager.sharedInstance().unregisterComponent(tree); scrollPane.getViewport().remove(tree); @@ -1126,6 +1183,7 @@ handle.start(event.getCount()); d.setVisible(true); } + }); } Index: refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/RefactoringUndoRedo.java --- refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/RefactoringUndoRedo.java Base (BASE) +++ refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/RefactoringUndoRedo.java Locally New @@ -0,0 +1,117 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.refactoring.spi.impl; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import org.openide.awt.UndoRedo; +import org.openide.util.ChangeSupport; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author Jan Becicka + */ +@ServiceProvider(service=UndoRedo.class, path="org/netbeans/modules/refactoring") +public class RefactoringUndoRedo implements UndoRedo { + + private UndoManager manager = UndoManager.getDefault(); + + private ChangeSupport pcs = new ChangeSupport(this); + + public RefactoringUndoRedo() { + manager.addChangeListener(new ChangeListener() { + + @Override + public void stateChanged(ChangeEvent e) { + pcs.fireChange(); + } + + }); + } + + @Override + public boolean canUndo() { + return manager.isUndoAvailable(); + } + + @Override + public boolean canRedo() { + return manager.isRedoAvailable(); + } + + @Override + public void undo() throws CannotUndoException { + manager.undo(); + } + + @Override + public void redo() throws CannotRedoException { + manager.redo(); + } + + @Override + public void addChangeListener(final ChangeListener l) { + pcs.addChangeListener(l); + + } + + @Override + public void removeChangeListener(ChangeListener l) { + pcs.removeChangeListener(l); + } + + @Override + public String getUndoPresentationName() { + return manager.getUndoDescription(); + } + + @Override + public String getRedoPresentationName() { + return manager.getRedoDescription(); + } + +} Index: refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/UndoAction.java --- refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/UndoAction.java Base (BASE) +++ refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/UndoAction.java Locally Deleted @@ -1,126 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * Oracle and Java are registered trademarks of Oracle and/or its affiliates. - * Other names may be trademarks of their respective owners. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, the - * "License"). You may not use this file except in compliance with the - * License. You can obtain a copy of the License at - * http://www.netbeans.org/cddl-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ - -package org.netbeans.modules.refactoring.spi.impl; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import javax.swing.Action; -import javax.swing.SwingUtilities; -import org.openide.awt.ActionID; -import org.openide.awt.ActionReference; -import org.openide.awt.ActionRegistration; -import org.openide.util.HelpCtx; -import org.openide.util.NbBundle; -import org.openide.util.actions.CallableSystemAction; - -@ActionID(id = "org.netbeans.modules.refactoring.spi.impl.UndoAction", category = "Refactoring") -@ActionRegistration(displayName = "#LBL_Undo") -@ActionReference(path = "Menu/Refactoring" , name = "UndoAction", position = 2000) -public class UndoAction extends CallableSystemAction implements PropertyChangeListener { - - private UndoManager undoManager; - - public UndoAction() { - putValue(Action.NAME, getString("LBL_Undo")); //NOI18N - putValue("noIconInMenu", Boolean.TRUE); // NOI18N - undoManager = UndoManager.getDefault(); - undoManager.addPropertyChangeListener(this); - updateState(); - } - - @Override - public void propertyChange (PropertyChangeEvent event) { - updateState(); - } - - private void updateState() { - String desc = undoManager.getUndoDescription(); - String name = getString("LBL_Undo"); //NOI18N - if (desc != null) { - name += " [" + desc + "]"; //NOI18N - } - - final String n = name; - final boolean b = undoManager.isUndoAvailable(); - Runnable r = new Runnable() { - @Override - public void run() { - setEnabled(b); - putValue(Action.NAME, n); - } - }; - - if (SwingUtilities.isEventDispatchThread()) { - r.run(); - } else { - SwingUtilities.invokeLater(r); - } - } - - private static final String getString(String key) { - return NbBundle.getMessage(UndoAction.class, key); - } - - @Override - public void performAction() { - undoManager.undo(); - undoManager.saveAll(); - } - - @Override - public org.openide.util.HelpCtx getHelpCtx() { - return HelpCtx.DEFAULT_HELP; - } - - @Override - public String getName() { - return (String) getValue(Action.NAME); - } - - @Override - protected boolean asynchronous() { - return true; - } -} Index: refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/UndoManager.java --- refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/UndoManager.java Base (BASE) +++ refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/UndoManager.java Locally Modified (Based On LOCAL) @@ -41,99 +41,65 @@ * Version 2 license, then the option applies only if the new code is * made subject to such option by the copyright holder. */ - package org.netbeans.modules.refactoring.spi.impl; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.LinkedList; -import java.util.Map; -import java.util.Set; -import javax.swing.SwingUtilities; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.text.AbstractDocument; -import javax.swing.text.Document; -import javax.swing.text.StyledDocument; -import org.netbeans.api.project.FileOwnerQuery; -import org.netbeans.api.project.Project; -import org.netbeans.api.project.ui.OpenProjects; -import org.netbeans.modules.refactoring.spi.BackupFacility; +import javax.swing.JOptionPane; +import javax.swing.event.ChangeListener; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; import org.netbeans.modules.refactoring.api.ProgressEvent; import org.netbeans.modules.refactoring.api.ProgressListener; import org.netbeans.modules.refactoring.api.RefactoringSession; -import org.openide.LifecycleManager; -import org.openide.cookies.EditorCookie; -import org.openide.filesystems.FileChangeAdapter; -import org.openide.filesystems.FileEvent; -import org.openide.filesystems.FileObject; -import org.openide.filesystems.FileRenameEvent; -import org.openide.loaders.DataObject; -import org.openide.text.CloneableEditorSupport; -import org.openide.text.NbDocument; -import org.openide.util.Exceptions; +import org.netbeans.modules.refactoring.spi.BackupFacility; +import org.openide.util.ChangeSupport; +import org.openide.util.NbBundle; +import org.openide.windows.WindowManager; /** * * @author Jan Becicka */ -public final class UndoManager extends FileChangeAdapter implements DocumentListener, PropertyChangeListener /*, GlobalPathRegistryListener */{ +public final class UndoManager { - /** stack of undo items */ + /** + * stack of undo items + */ private LinkedList> undoList; - - /** stack of redo items */ + /** + * stack of redo items + */ private LinkedList> redoList; - /** set of all CloneableEditorSupports */ - private final Set allCES = new HashSet(); - - private final Map fileObjectToCES = new HashMap(); - - /** map document -> CloneableEditorSupport */ - private final Map documentToCES = new HashMap(); - - /** map listener -> CloneableEditorSupport */ - private final Map> listenerToCES = new HashMap>(); - private boolean listenersRegistered = false; - - public static final String PROP_STATE = "state"; //NOI18N - - private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); - + private final ChangeSupport changeSupport = new ChangeSupport(this); private boolean wasUndo = false; private boolean wasRedo = false; private boolean transactionStart; - private boolean dontDeleteUndo = false; - private IdentityHashMap descriptionMap; private String description; private ProgressListener progress; - private static UndoManager instance; - private Set projects; - - public static UndoManager getDefault() { - if (instance==null) { + /** + * Singleton instance + * @return + */ + public static synchronized UndoManager getDefault() { + if (instance == null) { instance = new UndoManager(); } return instance; } - /** Creates a new instance of UndoManager */ + + /** + * Creates a new instance of UndoManager + */ private UndoManager() { undoList = new LinkedList>(); redoList = new LinkedList>(); descriptionMap = new IdentityHashMap(); - projects = new HashSet(); } private UndoManager(ProgressListener progress) { @@ -141,81 +107,83 @@ this.progress = progress; } + /** + * Setter for undo description. For instance "Rename" + * @param desc + */ public void setUndoDescription(String desc) { description = desc; } + /** + * Getter for undo description. + * @return + */ public String getUndoDescription() { - if (undoList.isEmpty()) return null; + if (undoList.isEmpty()) { + return null; + } return descriptionMap.get(undoList.getFirst()); } + /** + * Getter for Redo description. + * @return + */ public String getRedoDescription() { - if (redoList.isEmpty()) return null; + if (redoList.isEmpty()) { + return null; + } return descriptionMap.get(redoList.getFirst()); } - /** called to mark transaction start + /** + * called to mark transaction start */ public void transactionStarted() { transactionStart = true; - unregisterListeners(); - //RepositoryUpdater.getDefault().setListenOnChanges(false); } /** * called to mark end of transaction */ public void transactionEnded(boolean fail) { - try { description = null; - dontDeleteUndo = true; - if (fail && !undoList.isEmpty()) - undoList.removeFirst(); - else { + if (fail && !undoList.isEmpty()) { + //XXX todo + //undoList.removeFirst(); + } else { // [TODO] (jb) this code disables undos for changes using org.openide.src if (isUndoAvailable() && getUndoDescription() == null) { descriptionMap.remove(undoList.removeFirst()); - dontDeleteUndo = false; } - } - - invalidate(null); - dontDeleteUndo = false; - } finally { - if (SwingUtilities.isEventDispatchThread()) { - registerListeners(); - } else { - try { - SwingUtilities.invokeAndWait(new Runnable() { - - @Override - public void run() { - registerListeners(); + fireChange(); } - }); - } catch (InterruptedException ex) { - Exceptions.printStackTrace(ex); - } catch (InvocationTargetException ex) { - Exceptions.printStackTrace(ex); - } - } - fireStateChange(); - } - } - /** undo last transaction */ + /** + * undo last transaction + */ public void undo() { //System.out.println("************* Starting UNDO"); if (isUndoAvailable()) { + if (JOptionPane.showConfirmDialog( + WindowManager.getDefault().getMainWindow(), + NbBundle.getMessage(UndoManager.class, "MSG_ReallyUndo", getUndoDescription()), + NbBundle.getMessage(UndoManager.class, "MSG_ConfirmUndo"), + JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) { + throw new CannotUndoException(); + } + + Runnable run = new Runnable() { + + public void run() { boolean fail = true; try { transactionStarted(); wasUndo = true; - LinkedList undo = (LinkedList) undoList.getFirst(); + LinkedList undo = (LinkedList) undoList.getFirst(); fireProgressListenerStart(0, undo.size()); - undoList.removeFirst(); Iterator undoIterator = undo.iterator(); UndoItem item; redoList.addFirst(new LinkedList()); @@ -228,6 +196,7 @@ addItem(item); } } + undoList.removeFirst(); fail = false; } finally { try { @@ -235,24 +204,46 @@ transactionEnded(fail); } finally { fireProgressListenerStop(); - fireStateChange(); + fireChange(); } } + } + }; + +// if (SwingUtilities.isEventDispatchThread()) { +// ProgressUtils.runOffEventDispatchThread(run, +// "Undoing... ", +// new AtomicBoolean(), +// false); +// } else { + run.run(); +// } } + } - /** redo last undo + /** + * redo last undo */ public void redo() { //System.out.println("************* Starting REDO"); if (isRedoAvailable()) { + if (JOptionPane.showConfirmDialog( + WindowManager.getDefault().getMainWindow(), + NbBundle.getMessage(UndoManager.class, "MSG_ReallyRedo", getRedoDescription()), + NbBundle.getMessage(UndoManager.class, "MSG_ConfirmRedo"), + JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) { + throw new CannotRedoException(); + } + Runnable run = new Runnable() { + + public void run() { boolean fail = true; try { transactionStarted(); wasRedo = true; LinkedList redo = redoList.getFirst(); fireProgressListenerStart(1, redo.size()); - redoList.removeFirst(); Iterator redoIterator = redo.iterator(); UndoItem item; description = descriptionMap.remove(redo); @@ -264,6 +255,7 @@ addItem(item); } } + redoList.removeFirst(); fail = false; } finally { try { @@ -271,26 +263,41 @@ transactionEnded(fail); } finally { fireProgressListenerStop(); - fireStateChange(); + fireChange(); } } } + }; + +// if (SwingUtilities.isEventDispatchThread()) { +// ProgressUtils.runOffEventDispatchThread(run, +// "Redoing... ", +// new AtomicBoolean(), +// false); +// } else { + run.run(); +// } } + } - /** clean undo/redo stacks */ + /** + * clean undo/redo stacks + */ public void clear() { undoList.clear(); redoList.clear(); descriptionMap.clear(); BackupFacility.getDefault().clear(); - fireStateChange(); + fireChange(); } public void addItem(RefactoringSession session) { addItem(new SessionUndoItem(session)); } - /** add new item to undo/redo list */ + /** + * add new item to undo/redo list + */ private void addItem(UndoItem item) { if (wasUndo) { LinkedList redo = this.redoList.getFirst(); @@ -304,9 +311,10 @@ LinkedList undo = this.undoList.getFirst(); undo.addFirst(item); } - if (! (wasUndo || wasRedo)) + if (!(wasUndo || wasRedo)) { redoList.clear(); } + } public boolean isUndoAvailable() { return !undoList.isEmpty(); @@ -316,281 +324,51 @@ return !redoList.isEmpty(); } - public void addPropertyChangeListener(PropertyChangeListener pcl) { - pcs.addPropertyChangeListener(pcl); + public void addChangeListener(ChangeListener cl) { + changeSupport.addChangeListener(cl); } - public void removePropertyChangeListener(PropertyChangeListener pcl) { - pcs.removePropertyChangeListener(pcl); + public void removeChangeListener(ChangeListener cl) { + changeSupport.removeChangeListener(cl); } - private void fireStateChange() { - pcs.firePropertyChange(PROP_STATE, null, null); + private void fireChange() { + changeSupport.fireChange(); } - public void watch(Collection ceSupports, InvalidationListener l) { - synchronized (allCES) { - registerListeners(); - } - for (final CloneableEditorSupport ces : ceSupports) { - final Document d = ces.getDocument(); - if (d!=null) { - d.render(new Runnable() { - @Override - public void run() { - synchronized(allCES) { - if (allCES.add(ces)) { - ces.addPropertyChangeListener(UndoManager.this); - d.addDocumentListener(UndoManager.this); - documentToCES.put(d, ces); - Object o = d.getProperty(Document.StreamDescriptionProperty); - if (o instanceof DataObject) { - FileObject file = ((DataObject)o).getPrimaryFile(); - fileObjectToCES.put(file, ces); - Project p = FileOwnerQuery.getOwner(file); - if (p!= null) - projects.add(p); - } - } - } - } - }); - } else { - synchronized(allCES) { - if (allCES.add(ces)) { - ces.addPropertyChangeListener(UndoManager.this); - } - } - } - } - synchronized(allCES) { - if (l != null) { - listenerToCES.put(l, ceSupports); - } - } - } - - public void stopWatching(InvalidationListener l) { - //synchronized (undoStack) { - synchronized (allCES) { - listenerToCES.remove(l); - clearIfPossible(); - } - //} - } - - private static java.lang.reflect.Field undoRedo; - - static{ - try { - //obviously hack. See 108616 and 48427 - undoRedo = org.openide.text.CloneableEditorSupport.class.getDeclaredField("undoRedo"); //NOI18N - undoRedo.setAccessible(true); - } catch (NoSuchFieldException ex) { - Exceptions.printStackTrace(ex); - } catch (SecurityException ex) { - Exceptions.printStackTrace(ex); - } - } - - private void discardAllEdits(InvalidationListener l) { - for (CloneableEditorSupport s:listenerToCES==null||l==null?allCES:listenerToCES.get(l)) { - try { - org.openide.awt.UndoRedo.Manager manager = (org.openide.awt.UndoRedo.Manager) undoRedo.get(s); - if (manager!=null) { - //if manager not initialized - there is nothing to discard - //#114485 - manager.discardAllEdits(); - } - } catch (SecurityException ex) { - Exceptions.printStackTrace(ex); - } catch (IllegalArgumentException ex) { - Exceptions.printStackTrace(ex); - } catch (IllegalAccessException ex) { - Exceptions.printStackTrace(ex); - } - } - } - -// TODO: -// 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; - // TODO: - // GlobalPathRegistry.getDefault().addGlobalPathRegistryListener(this); - Util.addFileSystemsListener(this); - for (CloneableEditorSupport ces:allCES) { - ces.addPropertyChangeListener(this); - } - for (Document doc:documentToCES.keySet()) { - doc.addDocumentListener(this); - } - OpenProjects.getDefault().addPropertyChangeListener(this); - listenersRegistered = true; - } - - private void unregisterListeners() { - if (!listenersRegistered) return; - OpenProjects.getDefault().removePropertyChangeListener(this); - Util.removeFileSystemsListener(this); - //TODO: - //GlobalPathRegistry.getDefault().removeGlobalPathRegistryListener(this); - for (CloneableEditorSupport ces:allCES) { - ces.removePropertyChangeListener(this); - } - for (Document doc:documentToCES.keySet()) { - doc.removeDocumentListener(this); - } - listenersRegistered = false; - } - - private void invalidate(CloneableEditorSupport ces) { - synchronized (undoList) { - if (!(wasRedo || wasUndo) && !dontDeleteUndo) { - clear(); - } - synchronized (allCES) { - if (ces == null) { - // invalidate all - for (InvalidationListener lis:listenerToCES.keySet()) { - lis.invalidateObject(); - discardAllEdits(lis); - } - listenerToCES.clear(); - } else { - for (Iterator>> it = listenerToCES.entrySet().iterator(); it.hasNext();) { - Map.Entry> e = it.next(); - if ((e.getValue()).contains(ces)) { - 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(); - fileObjectToCES.clear(); - projects.clear(); - } - } - - // FileChangeAdapter ........................................................ - - @Override - public void fileChanged(FileEvent fe) { - CloneableEditorSupport ces = fileObjectToCES.get(fe.getFile()); - if (ces!=null) { - invalidate(ces); - } - } - - @Override - public void fileDeleted(FileEvent fe) { - fileChanged(fe); - } - - @Override - public void fileRenamed(FileRenameEvent fe) { - fileChanged(fe); - } - - // DocumentListener ......................................................... - - @Override - public void changedUpdate(DocumentEvent e) { - } - - @Override - public void insertUpdate(DocumentEvent e) { - invalidate(documentToCES.get(e.getDocument())); - } - - @Override - public void removeUpdate(DocumentEvent e) { - invalidate(documentToCES.get(e.getDocument())); - } - - private void documentStateChanged(CloneableEditorSupport ces) { - synchronized (allCES) { - 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 { - synchronized (allCES) { - registerListeners(); - } - } - } - private void fireProgressListenerStart(int type, int count) { stepCounter = 0; - if (progress == null) + if (progress == null) { return; + } progress.start(new ProgressEvent(this, ProgressEvent.START, type, count)); } - private int stepCounter = 0; - /** Notifies all registered listeners about the event. + + /** + * Notifies all registered listeners about the event. */ private void fireProgressListenerStep() { - if (progress == null) + if (progress == null) { return; + } progress.step(new ProgressEvent(this, ProgressEvent.STEP, 0, ++stepCounter)); } - /** Notifies all registered listeners about the event. + /** + * Notifies all registered listeners about the event. */ private void fireProgressListenerStop() { - if (progress == null) + if (progress == null) { return; + } progress.stop(new ProgressEvent(this, ProgressEvent.STOP)); } private interface UndoItem { + void undo(); + void redo(); } @@ -598,7 +376,7 @@ private RefactoringSession change; - public SessionUndoItem (RefactoringSession change) { + public SessionUndoItem(RefactoringSession change) { this.change = change; } @@ -612,16 +390,4 @@ change.doRefactoring(false); } } - - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (OpenProjects.PROPERTY_OPEN_PROJECTS.equals(evt.getPropertyName())) { - Set p = new HashSet(projects); - p.removeAll(Arrays.asList((Project[])evt.getNewValue())); - if (!p.isEmpty()) - invalidate(null); - } else if (EditorCookie.Observable.PROP_DOCUMENT.equals(evt.getPropertyName())) { - documentStateChanged((CloneableEditorSupport) evt.getSource()); } - } -} Index: refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/UndoWatcher.java --- refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/UndoWatcher.java Base (BASE) +++ refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/UndoWatcher.java Locally Deleted @@ -1,90 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * Oracle and Java are registered trademarks of Oracle and/or its affiliates. - * Other names may be trademarks of their respective owners. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, the - * "License"). You may not use this file except in compliance with the - * License. You can obtain a copy of the License at - * http://www.netbeans.org/cddl-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ -package org.netbeans.modules.refactoring.spi.impl; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import org.netbeans.modules.refactoring.api.RefactoringElement; -import org.netbeans.modules.refactoring.api.RefactoringSession; -import org.netbeans.modules.refactoring.spi.impl.UndoManager; -import org.openide.cookies.EditorCookie; -import org.openide.loaders.DataObject; -import org.openide.text.CloneableEditorSupport; -import org.openide.text.PositionBounds; - -/** - * - * @author Martin Matula, Daniel Prusa - */ -public class UndoWatcher { - - private static Collection extractCES(Collection elements) { - HashSet result = new HashSet(); - for (Iterator it = elements.iterator(); it.hasNext();) { - RefactoringElement e = (RefactoringElement) it.next(); - PositionBounds pb = e.getPosition(); - if (pb != null) { - CloneableEditorSupport ces = pb.getBegin().getCloneableEditorSupport(); - result.add(ces); - } - } - return result; - } - - public static void watch(RefactoringSession session, InvalidationListener l) { - UndoManager.getDefault().watch(extractCES(session.getRefactoringElements()), l); - } - - public static void stopWatching(InvalidationListener l) { - UndoManager.getDefault().stopWatching(l); - } - - public static void watch(DataObject o) { - EditorCookie ces = o.getCookie(EditorCookie.class); - assert ces instanceof CloneableEditorSupport; - UndoManager.getDefault().watch(Collections.singleton((CloneableEditorSupport)ces), null); - } -} Index: refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/UndoableWrapper.java --- refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/UndoableWrapper.java Base (BASE) +++ refactoring.api/src/org/netbeans/modules/refactoring/spi/impl/UndoableWrapper.java Locally New @@ -0,0 +1,206 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2011 Sun Microsystems, Inc. + */ +package org.netbeans.modules.refactoring.spi.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoableEdit; +import org.netbeans.api.editor.EditorRegistry; +import org.netbeans.api.editor.mimelookup.MimeRegistration; +import org.netbeans.editor.BaseDocument; +import org.netbeans.spi.editor.document.UndoableEditWrapper; +import org.openide.loaders.DataObject; +import org.openide.text.CloneableEditorSupport; + +/** + * + * @author Jan Becicka + */ +@MimeRegistration(mimeType="", service=UndoableEditWrapper.class) +public class UndoableWrapper implements UndoableEditWrapper { + + private AtomicBoolean active = new AtomicBoolean(); + private Map docToFirst = new HashMap(); + + public UndoableWrapper() { + } + + + @Override + public UndoableEdit wrap(UndoableEdit ed, Document doc) { + if (!active.get()) + return ed; + if (doc.getProperty(BaseDocument.StreamDescriptionProperty) == null) { + //no dataobject + return ed; + } + UndoableEditDelegate current = new UndoableEditDelegate(ed, (BaseDocument) doc); + UndoableEditDelegate first = docToFirst.get(doc); + if (first == null) { + docToFirst.put((BaseDocument) doc, current); + } + return current; + } + + public void close() { + for (UndoableEditDelegate first: docToFirst.values()) { + first.end(); + } + docToFirst.clear(); + } + + public void setActive(boolean b) { + active.set(b); + } + + public class UndoableEditDelegate implements UndoableEdit { + + private UndoManager undoManager; + private CloneableEditorSupport ces; + private UndoableEdit delegate; + private CompoundEdit inner; + + private UndoableEditDelegate(UndoableEdit ed, BaseDocument doc) { + undoManager = UndoManager.getDefault(); + DataObject dob = (DataObject) doc.getProperty(BaseDocument.StreamDescriptionProperty); + ces = dob.getLookup().lookup(CloneableEditorSupport.class); + //this.delegate = ed; + this.inner = new CompoundEdit(); + inner.addEdit(ed); + delegate = ed; + } + + @Override + public void undo() throws CannotUndoException { + JTextComponent focusedComponent = EditorRegistry.focusedComponent(); + if (focusedComponent != null) { + if (focusedComponent.getDocument() == ces.getDocument()) { + //call global undo only for focused component + undoManager.undo(); + } + } + //delegate.undo(); + inner.undo(); + } + + @Override + public boolean canUndo() { + //return delegate.canUndo(); + return inner.canUndo(); + } + + @Override + public void redo() throws CannotRedoException { + JTextComponent focusedComponent = EditorRegistry.focusedComponent(); + if (focusedComponent != null) { + if (focusedComponent.getDocument() == ces.getDocument()) { + //call global undo only for focused component + undoManager.redo(); + } + } + //delegate.redo(); + inner.redo(); + } + + @Override + public boolean canRedo() { + //return delegate.canRedo(); + return inner.canRedo(); + } + + @Override + public void die() { + //delegate.die(); + inner.die(); + } + + @Override + public boolean addEdit(UndoableEdit ue) { + if (ue instanceof UndoableEditDelegate) { + return inner.addEdit(((UndoableEditDelegate) ue).unwrap()); + } + return false; + //return delegate.addEdit(ue); + } + + public UndoableEdit unwrap() { + return delegate; + } + + @Override + public boolean replaceEdit(UndoableEdit ue) { + return inner.replaceEdit(ue); + //return delegate.replaceEdit(ue); + } + + @Override + public boolean isSignificant() { + return inner.isSignificant(); + //return delegate.isSignificant(); + } + + @Override + public String getPresentationName() { + return undoManager.getUndoDescription(); + } + + @Override + public String getUndoPresentationName() { + return undoManager.getUndoDescription(); + } + + @Override + public String getRedoPresentationName() { + return undoManager.getRedoDescription(); + } + + private void end() { + inner.end(); + } + } +} Index: refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/RetoucheCommit.java --- refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/RetoucheCommit.java Base (BASE) +++ refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/RetoucheCommit.java Locally Deleted @@ -1,156 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. - * - * Oracle and Java are registered trademarks of Oracle and/or its affiliates. - * Other names may be trademarks of their respective owners. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, the - * "License"). You may not use this file except in compliance with the - * License. You can obtain a copy of the License at - * http://www.netbeans.org/cddl-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun - * Microsystems, Inc. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ -package org.netbeans.modules.refactoring.java.plugins; - -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.netbeans.api.java.source.ModificationResult; -import org.netbeans.modules.refactoring.spi.BackupFacility; -import org.netbeans.modules.refactoring.spi.Transaction; -import org.openide.cookies.EditorCookie; -import org.openide.filesystems.FileObject; -import org.openide.filesystems.FileUtil; -import org.openide.loaders.DataObject; -import org.openide.loaders.DataObjectNotFoundException; -import org.openide.util.Exceptions; - -/** - * - * @author Jan Becicka - */ - -public class RetoucheCommit implements Transaction { - private static final Logger LOG = Logger.getLogger(RetoucheCommit.class.getName()); - List ids = new ArrayList(); - private boolean commited = false; - Collection results; - private Set newFiles; - - public RetoucheCommit(Collection results) { - this.results = results; - } - - @Override - public void commit() { - try { - if (commited) { - for (BackupFacility.Handle id:ids) { - try { - id.restore(); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - } else { - commited = true; - for (ModificationResult result:results) { - ids.add(BackupFacility.getDefault().backup(result.getModifiedFileObjects())); - if (newFiles == null) { - newFiles = new HashSet(); - } - newFiles.addAll(result.getNewFiles()); - result.commit(); - } - - openNewFiles(newFiles); - } - - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - - private boolean newFilesStored = false; - @Override - public void rollback() { - for (BackupFacility.Handle id:ids) { - try { - id.restore(); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - boolean localStored = false; - if (newFiles!=null) { - for (File f:newFiles) { - try { - FileObject fo = FileUtil.toFileObject(f); - if (!newFilesStored) { - ids.add(BackupFacility.getDefault().backup(fo)); - localStored = true; - } - fo.delete(); - } catch (IOException ex) { - Exceptions.printStackTrace(ex); - } - } - newFilesStored |= localStored; - } - } - - private static void openNewFiles(Set newFiles) { - if (newFiles == null) { - return; - } - for (File file : newFiles) { - FileObject fo = FileUtil.toFileObject(file); - if (fo != null) { - try { - DataObject dobj = DataObject.find(fo); - EditorCookie editor = dobj.getLookup().lookup(EditorCookie.class); - if (editor != null) { - editor.open(); - } - } catch (DataObjectNotFoundException ex) { - // not harmful - LOG.log(Level.INFO, ex.getMessage(), ex); - } - } - } - } -} - Index: refactoring.java/src/org/netbeans/modules/refactoring/java/spi/JavaModificationResult.java --- refactoring.java/src/org/netbeans/modules/refactoring/java/spi/JavaModificationResult.java Base (BASE) +++ refactoring.java/src/org/netbeans/modules/refactoring/java/spi/JavaModificationResult.java Locally New @@ -0,0 +1,81 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.refactoring.java.spi; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import org.netbeans.modules.refactoring.spi.ModificationResult; +import org.openide.filesystems.FileObject; + +/** + * + * @author Jan Becicka + */ +class JavaModificationResult implements ModificationResult { + + private org.netbeans.api.java.source.ModificationResult delegate; + + JavaModificationResult(org.netbeans.api.java.source.ModificationResult r) { + this.delegate = r; + } + + @Override + public Collection getModifiedFileObjects() { + return delegate.getModifiedFileObjects(); + } + + @Override + public Collection getNewFiles() { + return delegate.getNewFiles(); + } + + @Override + public void commit() throws IOException { + delegate.commit(); + } + + @Override + public String getResultingSource(FileObject file) throws IOException, IllegalArgumentException { + return delegate.getResultingSource(file); + } +} Index: refactoring.java/src/org/netbeans/modules/refactoring/java/spi/JavaRefactoringPlugin.java --- refactoring.java/src/org/netbeans/modules/refactoring/java/spi/JavaRefactoringPlugin.java Base (BASE) +++ refactoring.java/src/org/netbeans/modules/refactoring/java/spi/JavaRefactoringPlugin.java Locally Modified (Based On LOCAL) @@ -59,7 +59,7 @@ import org.netbeans.modules.refactoring.java.api.JavaRefactoringUtils; import org.netbeans.modules.refactoring.java.plugins.FindVisitor; import org.netbeans.modules.refactoring.java.plugins.JavaPluginUtils; -import org.netbeans.modules.refactoring.java.plugins.RetoucheCommit; +import org.netbeans.modules.refactoring.spi.RefactoringCommit; import org.netbeans.modules.refactoring.spi.ProgressProviderAdapter; import org.netbeans.modules.refactoring.spi.RefactoringElementsBag; import org.netbeans.modules.refactoring.spi.RefactoringPlugin; @@ -104,10 +104,18 @@ * @author Jan Becicka */ public static Transaction createTransaction(@NonNull Collection modifications) { - return new RetoucheCommit(modifications); + return new RefactoringCommit(createJavaModifications(modifications)); } + private static Collection createJavaModifications(Collection modifications) { + LinkedList result = new LinkedList(); + for (ModificationResult r:modifications) { + result.add(new JavaModificationResult(r)); + } + return result; + } + protected Problem preCheck(CompilationController javac) throws IOException { return null; }