From 6be494466b397670a0c21842dc3a66d1ad6d015d Mon Sep 17 00:00:00 2001 From: Marco Stornelli Date: Sat, 9 Mar 2019 10:22:10 +0100 Subject: Bug 303870 - Add override virtual methods functionality Added overridemethods package Change-Id: I73a8f0a396336acf7d3bbc8988e629da510ae781 Signed-off-by: Marco Stornelli Signed-off-by: pmarek --- core/org.eclipse.cdt.core/META-INF/MANIFEST.MF | 2 +- .../eclipse/cdt/core/CCorePreferenceConstants.java | 30 + .../internal/core/CCorePreferenceInitializer.java | 4 + .../ui/tests/refactoring/RefactoringTestSuite.java | 3 +- .../OverrideMethodsRefactoringTest.java | 606 +++++++++++++++++++++ core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF | 1 + core/org.eclipse.cdt.ui/plugin.properties | 3 + core/org.eclipse.cdt.ui/plugin.xml | 12 + .../ui/editor/ICEditorActionDefinitionIds.java | 6 + .../internal/ui/preferences/CodeStyleBlock.java | 21 +- .../ui/preferences/PreferencesMessages.java | 3 + .../ui/preferences/PreferencesMessages.properties | 3 + .../ImplementMethodRefactoring.java | 87 ++- .../MethodDefinitionInsertLocationFinder.java | 6 +- .../ui/refactoring/overridemethods/Messages.java | 38 ++ .../overridemethods/Messages.properties | 22 + .../ui/refactoring/overridemethods/Method.java | 197 +++++++ .../overridemethods/MethodCollector.java | 101 ++++ .../overridemethods/OverrideMethodsAction.java | 56 ++ .../overridemethods/OverrideMethodsInputPage.java | 218 ++++++++ .../OverrideMethodsRefactoring.java | 179 ++++++ .../OverrideMethodsRefactoringRunner.java | 37 ++ .../overridemethods/OverrideMethodsWizard.java | 34 ++ .../overridemethods/OverrideOptions.java | 46 ++ .../overridemethods/VirtualMethodContainer.java | 158 ++++++ .../overridemethods/VirtualMethodPrintData.java | 187 +++++++ .../overridemethods/VirtualMethodsASTVisitor.java | 162 ++++++ .../cdt/ui/actions/GenerateActionGroup.java | 25 +- .../cdt/ui/refactoring/actions/Messages.java | 1 + 29 files changed, 2223 insertions(+), 25 deletions(-) create mode 100755 core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/overridemethods/OverrideMethodsRefactoringTest.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Messages.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Messages.properties create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Method.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/MethodCollector.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsAction.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsInputPage.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsRefactoring.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsRefactoringRunner.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsWizard.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideOptions.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodContainer.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodPrintData.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodsASTVisitor.java (limited to 'core') diff --git a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF index d652f2cf9e3..e6bc529ced9 100644 --- a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.cdt.core; singleton:=true -Bundle-Version: 6.7.100.qualifier +Bundle-Version: 6.8.0.qualifier Bundle-Activator: org.eclipse.cdt.core.CCorePlugin Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java index 715f8709fa7..72e56a97fe3 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java @@ -368,6 +368,36 @@ public class CCorePreferenceConstants { */ public static final boolean DEFAULT_PLACE_CONST_RIGHT_OF_TYPE = false; + /** + * A named preference that specifies whether the override keyword should be added + * to method signature. + * + * @since 6.8 + */ + public static final String ADD_OVERRIDE_KEYWORD = "astwriter.addOverride"; //$NON-NLS-1$ + + /** + * A named preference that specifies whether the virtual keyword should be added + * to method signature. + * + * @since 6.8 + */ + public static final String PRESERVE_VIRTUAL_KEYWORD = "astwriter.preserveVirtual"; //$NON-NLS-1$ + + /** + * Default value for {@link #ADD_OVERRIDE_KEYWORD}. + * + * @since 6.8 + */ + public static final boolean DEFAULT_ADD_OVERRIDE_KEYWORD = false; + + /** + * Default value for {@link #PRESERVE_VIRTUAL_KEYWORD}. + * + * @since 6.8 + */ + public static final boolean DEFAULT_PRESERVE_VIRTUAL_KEYWORD = true; + /** * Returns the node in the preference in the given context. * diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java index f76419e5a7d..08be850207f 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java @@ -94,5 +94,9 @@ public class CCorePreferenceInitializer extends AbstractPreferenceInitializer { CCorePreferenceConstants.DEFAULT_SCALABILITY_MAXIMUM_TOKENS); defaultPreferences.putBoolean(CCorePreferenceConstants.PLACE_CONST_RIGHT_OF_TYPE, CCorePreferenceConstants.DEFAULT_PLACE_CONST_RIGHT_OF_TYPE); + defaultPreferences.putBoolean(CCorePreferenceConstants.ADD_OVERRIDE_KEYWORD, + CCorePreferenceConstants.DEFAULT_ADD_OVERRIDE_KEYWORD); + defaultPreferences.putBoolean(CCorePreferenceConstants.PRESERVE_VIRTUAL_KEYWORD, + CCorePreferenceConstants.DEFAULT_PRESERVE_VIRTUAL_KEYWORD); } } diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java index 5c4c250369c..c8a5731f2a5 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java @@ -24,6 +24,7 @@ import org.eclipse.cdt.ui.tests.refactoring.gettersandsetters.GenerateGettersAnd import org.eclipse.cdt.ui.tests.refactoring.hidemethod.HideMethodRefactoringTest; import org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest; import org.eclipse.cdt.ui.tests.refactoring.includes.IncludesTestSuite; +import org.eclipse.cdt.ui.tests.refactoring.overridemethods.OverrideMethodsRefactoringTest; import org.eclipse.cdt.ui.tests.refactoring.rename.RenameRegressionTests; import org.eclipse.cdt.ui.tests.refactoring.togglefunction.ToggleRefactoringTest; import org.eclipse.cdt.ui.tests.refactoring.utils.UtilTestSuite; @@ -37,7 +38,7 @@ import org.junit.runners.Suite; @Suite.SuiteClasses({ UtilTestSuite.class, RenameRegressionTests.class, ExtractFunctionRefactoringTest.class, ExtractConstantRefactoringTest.class, HideMethodRefactoringTest.class, GenerateGettersAndSettersTest.class, ImplementMethodRefactoringTest.class, ExtractLocalVariableRefactoringTest.class, ToggleRefactoringTest.class, - IncludesTestSuite.class, + IncludesTestSuite.class, OverrideMethodsRefactoringTest.class }) public class RefactoringTestSuite { diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/overridemethods/OverrideMethodsRefactoringTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/overridemethods/OverrideMethodsRefactoringTest.java new file mode 100755 index 00000000000..a805f0346d2 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/overridemethods/OverrideMethodsRefactoringTest.java @@ -0,0 +1,606 @@ +/******************************************************************************* + * Copyright (c) 2019 Marco Stornelli + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.refactoring.overridemethods; + +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; +import org.eclipse.cdt.internal.ui.refactoring.CRefactoring; +import org.eclipse.cdt.internal.ui.refactoring.overridemethods.Method; +import org.eclipse.cdt.internal.ui.refactoring.overridemethods.OverrideMethodsRefactoring; +import org.eclipse.cdt.ui.tests.refactoring.RefactoringTestBase; + +import junit.framework.Test; + +/** + * Tests for override methods + */ +public class OverrideMethodsRefactoringTest extends RefactoringTestBase { + + private String[] selectedMethods; + private OverrideMethodsRefactoring refactoring; + private boolean addOverride = false; + private boolean preserveVirtual = true; + + public OverrideMethodsRefactoringTest() { + super(); + } + + public OverrideMethodsRefactoringTest(String name) { + super(name); + } + + public static Test suite() { + return suite(OverrideMethodsRefactoringTest.class); + } + + @Override + protected CRefactoring createRefactoring() { + refactoring = new OverrideMethodsRefactoring(getSelectedTranslationUnit(), getSelection(), getCProject()); + return refactoring; + } + + @Override + protected void simulateUserInput() { + if (selectedMethods != null) { + Map> map = refactoring.getMethodContainer().getInitialInput(); + for (Map.Entry> entry : map.entrySet()) { + List methods = entry.getValue(); + for (Method m : methods) { + for (String name : selectedMethods) { + if (m.toString().equals(name)) + refactoring.getPrintData().addMethod(m); + } + } + } + } + refactoring.getOptions().setAddOverride(addOverride); + refactoring.getOptions().setPreserveVirtual(preserveVirtual); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const = 0; + //}; + //class X: public Base { + //public: + // X(); + // /*$*//*$$*/ + //}; + //==================== + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const = 0; + //}; + //class X: public Base { + //public: + // X(); + // virtual void baseFunc() const; + //}; + // + //inline void X::baseFunc() const { + //} + public void testWithHeaderOnly() throws Exception { + selectedMethods = new String[] { "baseFunc()const" }; + assertRefactoringSuccess(); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const = 0; + //}; + //class X: public Base { + //public: + // X(); + // /*$*//*$$*/ + //}; + //==================== + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const = 0; + //}; + //class X: public Base { + //public: + // X(); + // virtual void baseFunc() const; + //}; + + //A.cpp + //#include "A.h" + //==================== + //#include "A.h" + // + //void X::baseFunc() const { + //} + public void testWithHeaderAndSource() throws Exception { + selectedMethods = new String[] { "baseFunc()const" }; + assertRefactoringSuccess(); + } + + //A.h + //namespace FIRST { + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc(Base* ptr) const = 0; + //}; + //}; + //namespace SECOND { + //class X: public FIRST::Base { + //public: + // X(); + // /*$*//*$$*/ + //}; + //}; + //==================== + //namespace FIRST { + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc(Base* ptr) const = 0; + //}; + //}; + //namespace SECOND { + //class X: public FIRST::Base { + //public: + // X(); + // virtual void baseFunc(FIRST::Base* ptr) const; + //}; + //}; + + //A.cpp + //#include "A.h" + //==================== + //#include "A.h" + // + //void SECOND::X::baseFunc(FIRST::Base* ptr) const { + //} + public void testWithMixedNamespaceHeaderAndSource() throws Exception { + selectedMethods = new String[] { "baseFunc(FIRST::Base *)const" }; + assertRefactoringSuccess(); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const = 0; + //}; + //class X: public Base { + //public: + // X(); + // /*$*//*$$*/ + //}; + //==================== + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const = 0; + //}; + //class X: public Base { + //public: + // X(); + // void baseFunc() const; + //}; + // + //inline void X::baseFunc() const { + //} + public void testIgnoringVirtual() throws Exception { + preserveVirtual = false; + selectedMethods = new String[] { "baseFunc()const" }; + assertRefactoringSuccess(); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const = 0; + //}; + //class X: public Base { + //public: + // X(); + // /*$*//*$$*/ + //}; + //==================== + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const = 0; + //}; + //class X: public Base { + //public: + // X(); + // virtual void baseFunc() const override; + //}; + // + //inline void X::baseFunc() const { + //} + public void testAddingOverrideVirtual() throws Exception { + addOverride = true; + selectedMethods = new String[] { "baseFunc()const" }; + assertRefactoringSuccess(); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const = 0; + //}; + //template + //class X: public Base { + //public: + // X(); + // /*$*//*$$*/ + //}; + //==================== + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const = 0; + //}; + //template + //class X: public Base { + //public: + // X(); + // virtual void baseFunc() const; + //}; + // + //template + //inline void X::baseFunc() const { + //} + public void testWithTemplateClass() throws Exception { + selectedMethods = new String[] { "baseFunc()const" }; + assertRefactoringSuccess(); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const = 0; + //}; + //class X { + //public: + // X(); + // class Internal: public Base { + // public: + // /*$*//*$$*/ + // }; + //}; + //==================== + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const = 0; + //}; + //class X { + //public: + // X(); + // class Internal: public Base { + // public: + // virtual void baseFunc() const; + // }; + //}; + + //A.cpp + //#include "A.h" + //==================== + //#include "A.h" + // + //void X::Internal::baseFunc() const { + //} + public void testWithNestedClass() throws Exception { + selectedMethods = new String[] { "baseFunc()const" }; + assertRefactoringSuccess(); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const = 0; + //}; + //class X: public Base { + //public: + // X(); + //}; + ///*$*//*$$*/ + //==================== + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const = 0; + //}; + //class X: public Base { + //public: + // X(); + //}; + public void testWithNoSelection() throws Exception { + selectedMethods = new String[] { "baseFunc()const" }; + assertRefactoringFailure(); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // void baseFunc() const; + //}; + //class X: public Base { + //public: + // X(); + ///*$*//*$$*/ + //}; + //==================== + //class Base { + //public: + // virtual ~Base(); + // void baseFunc() const; + //}; + //class X: public Base { + //public: + // X(); + //}; + public void testWithNoMethods() throws Exception { + selectedMethods = new String[] { "baseFunc()const" }; + assertRefactoringFailure(); + } + + //A.h + //class X { + //public: + // X(); + ///*$*//*$$*/ + //}; + //==================== + //class X { + //public: + // X(); + //}; + public void testWithNoBaseClass() throws Exception { + assertRefactoringFailure(); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const throw (int) = 0; + //}; + //class X: public Base { + //public: + // X(); + // /*$*//*$$*/ + //}; + //==================== + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const throw (int) = 0; + //}; + //class X: public Base { + //public: + // X(); + // virtual void baseFunc() const throw (int); + //}; + // + //inline void X::baseFunc() const throw (int) { + //} + public void testWithThrowNoEmpty() throws Exception { + selectedMethods = new String[] { "baseFunc()const" }; + assertRefactoringSuccess(); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const throw () = 0; + //}; + //class X: public Base { + //public: + // X(); + // /*$*//*$$*/ + //}; + //==================== + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const throw () = 0; + //}; + //class X: public Base { + //public: + // X(); + // virtual void baseFunc() const throw (); + //}; + // + //inline void X::baseFunc() const throw () { + //} + public void testWithThrowEmpty() throws Exception { + selectedMethods = new String[] { "baseFunc()const" }; + assertRefactoringSuccess(); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const noexcept = 0; + //}; + //class X: public Base { + //public: + // X(); + // /*$*//*$$*/ + //}; + //==================== + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() const noexcept = 0; + //}; + //class X: public Base { + //public: + // X(); + // virtual void baseFunc() const noexcept; + //}; + // + //inline void X::baseFunc() const { + //} + public void testWithNoExcept() throws Exception { + selectedMethods = new String[] { "baseFunc()const" }; + assertRefactoringSuccess(); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc1() const = 0; + // virtual void baseFunc2() const = 0; + //}; + //class X: public Base { + //public: + // X(); + // /*$*//*$$*/ + //}; + //==================== + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc1() const = 0; + // virtual void baseFunc2() const = 0; + //}; + //class X: public Base { + //public: + // X(); + // virtual void baseFunc2() const; + // virtual void baseFunc1() const; + //}; + // + //inline void X::baseFunc2() const { + //} + // + //inline void X::baseFunc1() const { + //} + public void testWithMultipleMethods() throws Exception { + selectedMethods = new String[] { "baseFunc1()const", "baseFunc2()const" }; + assertRefactoringSuccess(); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() && = 0; + //}; + //class X: public Base { + //public: + // X(); + // /*$*//*$$*/ + //}; + //==================== + //class Base { + //public: + // virtual ~Base(); + // virtual void baseFunc() && = 0; + //}; + //class X: public Base { + //public: + // X(); + // virtual void baseFunc() &&; + //}; + // + //inline void X::baseFunc() && { + //} + public void testWithRefQualifier() throws Exception { + selectedMethods = new String[] { "baseFunc()&&" }; + assertRefactoringSuccess(); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // virtual void* baseFunc(void* ptr) const = 0; + //}; + //class X: public Base { + //public: + // X(); + // /*$*//*$$*/ + //}; + //==================== + //class Base { + //public: + // virtual ~Base(); + // virtual void* baseFunc(void* ptr) const = 0; + //}; + //class X: public Base { + //public: + // X(); + // virtual void* baseFunc(void* ptr) const; + //}; + + //A.cpp + //#include "A.h" + //==================== + //#include "A.h" + // + //void* X::baseFunc(void* ptr) const { + //} + public void testWithPointers() throws Exception { + selectedMethods = new String[] { "baseFunc(void *)const" }; + assertRefactoringSuccess(); + } + + //A.h + //class Base { + //public: + // virtual ~Base(); + // virtual void* baseFunc(void* ptr) const = 0, method2(); + //}; + //class X: public Base { + //public: + // X(); + // /*$*//*$$*/ + //}; + //==================== + //class Base { + //public: + // virtual ~Base(); + // virtual void* baseFunc(void* ptr) const = 0, method2(); + //}; + //class X: public Base { + //public: + // X(); + // virtual void* baseFunc(void* ptr) const; + //}; + + //A.cpp + //#include "A.h" + //==================== + //#include "A.h" + // + //void* X::baseFunc(void* ptr) const { + //} + public void testWithMultipleMethodsOnSameLine() throws Exception { + selectedMethods = new String[] { "baseFunc(void *)const" }; + assertRefactoringSuccess(); + } +} diff --git a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF index 689e852e668..f527ef584fd 100644 --- a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF @@ -44,6 +44,7 @@ Export-Package: org.eclipse.cdt.internal.corext;x-internal:=true, org.eclipse.cdt.internal.ui.refactoring.hidemethod;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.implementmethod;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.includes;x-friends:="org.eclipse.cdt.ui.tests", + org.eclipse.cdt.internal.ui.refactoring.overridemethods;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.rename;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.togglefunction;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.utils;x-friends:="org.eclipse.cdt.ui.tests", diff --git a/core/org.eclipse.cdt.ui/plugin.properties b/core/org.eclipse.cdt.ui/plugin.properties index 1ca78dfdc6d..9aac275b99c 100644 --- a/core/org.eclipse.cdt.ui/plugin.properties +++ b/core/org.eclipse.cdt.ui/plugin.properties @@ -176,6 +176,8 @@ ActionDefinition.implementMethod.name= Implement Method - Source Generation\u002 ActionDefinition.implementMethod.description= Implements a method for a selected method declaration ActionDefinition.gettersAndSetters.name = Generate Getters and Setters... ActionDefinition.gettersAndSetters.description = Generates getters and setters for a selected field +ActionDefinition.overrideMethods.name = Override Methods... +ActionDefinition.overrideMethods.description = Generates override methods for a selected class ActionDefinition.surroundWith.quickMenu.name= Surround With Quick Menu ActionDefinition.surroundWith.quickMenu.description= Shows the Surround With quick menu @@ -193,6 +195,7 @@ Refactoring.toggleFunction.label=Toggle Function Refactoring.hideMethod.label=Hide Method... Refactoring.implementMethod.label=Impl&ement Method... Refactoring.gettersAndSetters.label=Gene&rate Getters and Setters... +Refactoring.overrideMethods.label=Override Methods... Source.menu.label = &Source diff --git a/core/org.eclipse.cdt.ui/plugin.xml b/core/org.eclipse.cdt.ui/plugin.xml index 802941373be..909d260636f 100644 --- a/core/org.eclipse.cdt.ui/plugin.xml +++ b/core/org.eclipse.cdt.ui/plugin.xml @@ -1937,6 +1937,13 @@ id="org.eclipse.cdt.ui.actions.ImplementMethod" retarget="true"> + + + override methods action + * (value "org.eclipse.cdt.ui.refactor.override.methods"). + */ + public static final String OVERRIDE_METHODS = "org.eclipse.cdt.ui.refactor.override.methods"; //$NON-NLS-1$ + /** * Action definition ID of the refactor -> extract local variable action * (value "org.eclipse.cdt.ui.refactor.extract.local.variable"). diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeStyleBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeStyleBlock.java index 170351745ab..d702098eb1d 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeStyleBlock.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeStyleBlock.java @@ -37,10 +37,15 @@ class CodeStyleBlock extends OptionsConfigurationBlock { PreferenceConstants.FUNCTION_PASS_OUTPUT_PARAMETERS_BY_POINTER); private static final Key PLACE_CONST_RIGHT_OF_TYPE = getKey(CCorePlugin.PLUGIN_ID, CCorePreferenceConstants.PLACE_CONST_RIGHT_OF_TYPE); + private static final Key ADD_OVERRIDE_KEYWORD = getKey(CCorePlugin.PLUGIN_ID, + CCorePreferenceConstants.ADD_OVERRIDE_KEYWORD); + private static final Key PRESERVE_VIRTUAL_KEYWORD = getKey(CCorePlugin.PLUGIN_ID, + CCorePreferenceConstants.PRESERVE_VIRTUAL_KEYWORD); private static Key[] getAllKeys() { return new Key[] { CLASS_MEMBER_ASCENDING_VISIBILITY_ORDER, FUNCTION_OUTPUT_PARAMETERS_BEFORE_INPUT, - FUNCTION_PASS_OUTPUT_PARAMETERS_BY_POINTER, PLACE_CONST_RIGHT_OF_TYPE, }; + FUNCTION_PASS_OUTPUT_PARAMETERS_BY_POINTER, PLACE_CONST_RIGHT_OF_TYPE, ADD_OVERRIDE_KEYWORD, + PRESERVE_VIRTUAL_KEYWORD }; } public CodeStyleBlock(IStatusChangeListener context, IProject project, IWorkbenchPreferenceContainer container) { @@ -69,6 +74,9 @@ class CodeStyleBlock extends OptionsConfigurationBlock { composite = addSubsection(control, PreferencesMessages.CodeStyleBlock_const_keyword_placement); fillConstPlacementsSections(composite); + composite = addSubsection(control, PreferencesMessages.CodeStyleBlock_function_overridden_methods); + fillOverriddenSection(composite); + scrolled.setContent(control); final Point size = control.computeSize(SWT.DEFAULT, SWT.DEFAULT); scrolled.setMinSize(size.x, size.y); @@ -119,6 +127,17 @@ class CodeStyleBlock extends OptionsConfigurationBlock { 0); } + private void fillOverriddenSection(Composite composite) { + GridLayout layout = new GridLayout(); + layout.numColumns = 3; + composite.setLayout(layout); + + addCheckBox(composite, PreferencesMessages.CodeStyleBlock_add_override_keyword, ADD_OVERRIDE_KEYWORD, + TRUE_FALSE, 0); + addCheckBox(composite, PreferencesMessages.CodeStyleBlock_preserve_virtual, PRESERVE_VIRTUAL_KEYWORD, + TRUE_FALSE, 0); + } + @Override protected void validateSettings(Key changedKey, String oldValue, String newValue) { } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java index fcb869f5075..4298052a36c 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java @@ -224,6 +224,9 @@ public final class PreferencesMessages extends NLS { public static String CodeStyleBlock_const_keyword_placement; public static String CodeStyleBlock_const_left; public static String CodeStyleBlock_const_right; + public static String CodeStyleBlock_function_overridden_methods; + public static String CodeStyleBlock_preserve_virtual; + public static String CodeStyleBlock_add_override_keyword; public static String TodoTaskPreferencePage_title; public static String TodoTaskPreferencePage_description; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties index 1e56f82004a..7a42b567672 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties @@ -259,6 +259,9 @@ CodeStyleBlock_pass_by_pointer=Pass by poi&nter CodeStyleBlock_const_keyword_placement=Placement of the const keyword: CodeStyleBlock_const_left=&Left of type e.g. "const int n{};" CodeStyleBlock_const_right=&Right of type e.g. "int const n{};" +CodeStyleBlock_function_overridden_methods=Overridden methods +CodeStyleBlock_preserve_virtual=Add virtual keyword on overridden methods +CodeStyleBlock_add_override_keyword=Add override keyword on overridden methods # Task tags. TodoTaskPreferencePage_title=Task Tags diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoring.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoring.java index 6574bb089ae..5d60ae6252c 100755 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoring.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoring.java @@ -171,12 +171,58 @@ public class ImplementMethodRefactoring extends CRefactoring { List methodsToImplement = data.getMethodsToImplement(); SubMonitor sm = SubMonitor.convert(pm, 4 * methodsToImplement.size()); for (MethodToImplementConfig config : methodsToImplement) { - createDefinition(collector, config, sm.newChild(4)); + createDefinition(collector, config, sm.newChild(4), -1); } } + /** + * Utility method to collect modifications from another Refactoring + * @param pm The progress monitor + * @param collector The collector + * @param methods List of methods + * @param functionOffset A function offset to determine fully qualified names + * @throws CoreException + * @throws OperationCanceledException + */ + public void collectModifications(IProgressMonitor pm, ModificationCollector collector, + List methods, int functionOffset) throws CoreException, OperationCanceledException { + data.setMethodDeclarations(methods); + for (MethodToImplementConfig config : data.getMethodDeclarations()) { + config.setChecked(true); + } + List methodsToImplement = data.getMethodsToImplement(); + SubMonitor sm = SubMonitor.convert(pm, 4 * methodsToImplement.size()); + for (MethodToImplementConfig config : methodsToImplement) { + createDefinition(collector, config, sm.newChild(4), functionOffset); + } + } + + /** + * Create definition for a method + * @param collector A modification collector + * @param config The method to be inserted + * @param subMonitor A sub monitor for the progress + * @throws CoreException + * @throws OperationCanceledException + */ protected void createDefinition(ModificationCollector collector, MethodToImplementConfig config, IProgressMonitor subMonitor) throws CoreException, OperationCanceledException { + createDefinition(collector, config, subMonitor, -1); + } + + /** + * Create definition for a method + * @param collector A modification collector + * @param config The method to be inserted + * @param subMonitor A sub monitor for the progress + * @param functionOffset The node offset can be explicitly defined with this parameter, + * worth when the declarator does not have a valid offset yet. A negative number + * can be used to use the node offset of method instead, as returned by getFileLocation().getNodeOffset() + * @throws CoreException + * @throws OperationCanceledException + */ + protected void createDefinition(ModificationCollector collector, MethodToImplementConfig config, + IProgressMonitor subMonitor, int functionOffset) throws CoreException, OperationCanceledException { if (subMonitor.isCanceled()) { throw new OperationCanceledException(); } @@ -196,7 +242,7 @@ public class ImplementMethodRefactoring extends CRefactoring { } IASTNode nodeToInsertBefore = insertLocation.getNodeToInsertBefore(); - IASTNode createdMethodDefinition = createFunctionDefinition(ast, decl, insertLocation); + IASTNode createdMethodDefinition = createFunctionDefinition(ast, decl, insertLocation, functionOffset); subMonitor.worked(1); ASTRewrite methodRewrite = translationUnitRewrite.insertBefore(parent, nodeToInsertBefore, createdMethodDefinition, null); @@ -243,8 +289,18 @@ public class ImplementMethodRefactoring extends CRefactoring { return insertLocation; } + /** + * Create the function definition + * @param unit The translation unit + * @param methodDeclaration The method to be inserted + * @param insertLocation The position for the insert operation + * @param functionOffset A function offset to determine fully qualified names. A negative number + * can be used to use the node offset of method as returned by getFileLocation().getNodeOffset() + * @return + * @throws CoreException + */ private IASTDeclaration createFunctionDefinition(IASTTranslationUnit unit, IASTSimpleDeclaration methodDeclaration, - InsertLocation insertLocation) throws CoreException { + InsertLocation insertLocation, int functionOffset) throws CoreException { IASTDeclSpecifier declSpecifier = methodDeclaration.getDeclSpecifier().copy(CopyStyle.withLocations); ICPPASTFunctionDeclarator functionDeclarator = (ICPPASTFunctionDeclarator) methodDeclaration .getDeclarators()[0]; @@ -264,7 +320,8 @@ public class ImplementMethodRefactoring extends CRefactoring { declSpecifier.setStorageClass(IASTDeclSpecifier.sc_unspecified); } - ICPPASTQualifiedName qName = createQualifiedNameFor(functionDeclarator, declarationParent, insertLocation); + ICPPASTQualifiedName qName = createQualifiedNameFor(functionDeclarator, declarationParent, insertLocation, + functionOffset); createdMethodDeclarator = nodeFactory.newFunctionDeclarator(qName); createdMethodDeclarator.setConst(functionDeclarator.isConst()); @@ -299,12 +356,22 @@ public class ImplementMethodRefactoring extends CRefactoring { return functionDefinition; } + /** + * Create the fully qualified name for the declaration + * @param functionDeclarator The function declaration + * @param declarationParent Parent of declaration + * @param insertLocation Insert position + * @param functionOffset A function offset to determine fully qualified names. A negative number + * can be used to use the node offset of method as returned by getFileLocation().getNodeOffset() + * @return The fully qualified name + * @throws CoreException + */ private ICPPASTQualifiedName createQualifiedNameFor(IASTFunctionDeclarator functionDeclarator, - IASTNode declarationParent, InsertLocation insertLocation) throws CoreException { + IASTNode declarationParent, InsertLocation insertLocation, int functionOffset) throws CoreException { int insertOffset = insertLocation.getInsertPosition(); return NameHelper.createQualifiedNameFor(functionDeclarator.getName(), tu, - functionDeclarator.getFileLocation().getNodeOffset(), insertLocation.getTranslationUnit(), insertOffset, - refactoringContext); + functionOffset >= 0 ? functionOffset : functionDeclarator.getFileLocation().getNodeOffset(), + insertLocation.getTranslationUnit(), insertOffset, refactoringContext); } public ImplementMethodData getRefactoringData() { @@ -343,10 +410,14 @@ public class ImplementMethodRefactoring extends CRefactoring { if (isOneOrMoreImplementationInHeader(subProgressMonitor)) { result.addInfo(Messages.ImplementMethodRefactoring_NoImplFile); } - Checks.addModifiedFilesToChecker(getAllFilesToModify(), checkContext); + finalConditions(checkContext); return result; } + public void finalConditions(CheckConditionsContext checkContext) { + Checks.addModifiedFilesToChecker(getAllFilesToModify(), checkContext); + } + private boolean isOneOrMoreImplementationInHeader(IProgressMonitor subProgressMonitor) throws CoreException { for (MethodToImplementConfig config : data.getMethodsToImplement()) { IASTSimpleDeclaration decl = config.getDeclaration(); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/MethodDefinitionInsertLocationFinder.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/MethodDefinitionInsertLocationFinder.java index 9261859b492..88ad130e390 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/MethodDefinitionInsertLocationFinder.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/MethodDefinitionInsertLocationFinder.java @@ -161,7 +161,8 @@ public class MethodDefinitionInsertLocationFinder { if (pm != null && pm.isCanceled()) { return outputDeclarations; } - if (decl.getFileLocation().getStartingLineNumber() >= methodPosition.getStartingLineNumber()) { + if (methodPosition != null + && decl.getFileLocation().getStartingLineNumber() >= methodPosition.getStartingLineNumber()) { break; } if (isMemberFunctionDeclaration(decl)) { @@ -176,7 +177,8 @@ public class MethodDefinitionInsertLocationFinder { private static Collection getAllFollowingSimpleDeclarationsFromClass( IASTDeclaration[] declarations, IASTFileLocation methodPosition, IProgressMonitor pm) { ArrayList outputDeclarations = new ArrayList<>(); - + if (methodPosition == null) + return outputDeclarations; if (declarations.length >= 0) { for (IASTDeclaration decl : declarations) { if (pm != null && pm.isCanceled()) { diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Messages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Messages.java new file mode 100644 index 00000000000..45d6ba6c3da --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Messages.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2017 Pavel Marek + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Pavel Marek - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.overridemethods; + +import org.eclipse.osgi.util.NLS; + +final class Messages extends NLS { + public static String OverrideMethodsInputPage_Name; + public static String OverrideMethodsInputPage_Header; + public static String OverrideMethodsInputPage_SelectAll; + public static String OverrideMethodsInputPage_DeselectAll; + public static String OverrideMethodsRefactoring_SelNotInClass; + public static String OverrideMethodsRefactoring_NoMethods; + public static String OverrideMethodsRefactoring_PreserveVirtual; + public static String OverrideMethodsRefactoring_AddOverride; + public static String OverrideMethodsRefactoring_LinkDescription; + public static String OverrideMethodsRefactoring_LinkTooltip; + public static String OverrideMethods_label; + + static { + NLS.initializeMessages(Messages.class.getName(), Messages.class); + } + + // Do not instantiate + private Messages() { + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Messages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Messages.properties new file mode 100644 index 00000000000..6d8f26aa6ea --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Messages.properties @@ -0,0 +1,22 @@ +############################################################################### +# Copyright (c) 2019 Marco Stornelli +# +# This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +############################################################################### +OverrideMethodsInputPage_Name=Override Methods +OverrideMethodsInputPage_Header=Select methods to override: +OverrideMethodsInputPage_SelectAll=Select All +OverrideMethodsInputPage_DeselectAll=Deselect All +OverrideMethodsRefactoring_SelNotInClass=Selection not inside class. +OverrideMethodsRefactoring_NoMethods=No methods to override. +OverrideMethodsRefactoring_PreserveVirtual=Preserve 'virtual' keyword +OverrideMethodsRefactoring_AddOverride=Add 'override' keyword +OverrideMethodsRefactoring_LinkDescription=The options may be configured on the Code Style preference page. +OverrideMethodsRefactoring_LinkTooltip=Show the code style preferences. +OverrideMethods_label=Override Methods... diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Method.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Method.java new file mode 100644 index 00000000000..7da8987a12e --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Method.java @@ -0,0 +1,197 @@ +/******************************************************************************* + * Copyright (c) 2017 Pavel Marek + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Pavel Marek - initial API and implementation + * Marco Stornelli - Improvements + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.overridemethods; + +import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.IASTDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTPointerOperator; +import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTTypeId; +import org.eclipse.cdt.core.dom.ast.INodeFactory; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExpression; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVirtSpecifier.SpecifierKind; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPNodeFactory; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPParameter; +import org.eclipse.cdt.core.dom.rewrite.DeclarationGenerator; +import org.eclipse.cdt.internal.ui.refactoring.CRefactoringContext; +import org.eclipse.cdt.internal.ui.refactoring.utils.DefinitionFinder; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.OperationCanceledException; + +/** + * Wrapper for ICPPMethod + */ +public class Method { + private IASTDeclSpecifier fDeclSpecifier; + private ICPPMethod fMethod; + private OverrideOptions fOptions; + + /** + * Accepts only methods declared as virtual. + * @param method The ICPPMethod to be wrapped + * @param declSpecifier The class declaration specifier + * @param options Override options + */ + public Method(ICPPMethod method, IASTDeclSpecifier declSpecifier, OverrideOptions options) { + fMethod = method; + fOptions = options; + fDeclSpecifier = declSpecifier; + } + + /** + * Accepts only methods declared as virtual. + * @param method The ICPPMethod to be wrapped + * @param options Override options + */ + public Method(ICPPMethod method, OverrideOptions options) { + fMethod = method; + fOptions = options; + fDeclSpecifier = null; + } + + /** + * Two methods are considered equal if they have same signature ie. name + * and types of parameters in same order. + */ + @Override + public int hashCode() { + StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.append(fMethod.getName()); + for (ICPPParameter parameter : fMethod.getParameters()) { + stringBuilder.append(parameter.getType()); + + } + return stringBuilder.toString().hashCode(); + } + + @Override + public boolean equals(Object o) { + return this.hashCode() == o.hashCode(); + } + + /** + * Get the wrapped method + * @return The method + */ + public ICPPMethod getMethod() { + return fMethod; + } + + /** + * Accepts only methods declared as virtual. + * @param fMethod + */ + public void setMethod(ICPPMethod fMethod) { + this.fMethod = fMethod; + } + + @Override + public String toString() { + return fMethod.toString(); + } + + /** + * Get the class declaration specifier + * @return The class declaration specifier + */ + public IASTDeclSpecifier getDeclSpecifier() { + return fDeclSpecifier; + } + + /** + * Create a IASTNode for this method + * @param context The refactoring context + * @return The IASTNode for the method declaration + * @throws OperationCanceledException + * @throws CoreException + */ + public IASTNode createNode(CRefactoringContext context) throws OperationCanceledException, CoreException { + ICPPFunctionType functionType = fMethod.getDeclaredType(); + ICPPParameter[] parameters = fMethod.getParameters(); + IASTName declaration = DefinitionFinder.getMemberDeclaration(fMethod, getDeclSpecifier().getTranslationUnit(), + context, null); + INodeFactory factory = getDeclSpecifier().getTranslationUnit().getASTNodeFactory(); + DeclarationGenerator declGen = DeclarationGenerator.create(factory); + if (declaration == null) + return null; + + IASTDeclarator declarator = (IASTDeclarator) declaration.getParent(); + IASTNode parent = declarator.getParent(); + if (!(parent instanceof IASTSimpleDeclaration)) + return null; + + /** + * We can't just copy the original nodes here but we need to create a new node. We can't do it + * because the original node could lack some information needed in the clone. Example: the node + * in parent has a parameter to an object inside a namespace but namespace miss because the interface + * class is declared in the same namespace too, but in this case the new method of child class may needs + * of fully qualified name for the parameter, so a plain copy doesn't work. + */ + IASTStandardFunctionDeclarator newDeclarator = factory.newFunctionDeclarator(declarator.getName().copy()); + IASTDeclSpecifier newDeclSpec = declGen.createDeclSpecFromType(functionType); + if (newDeclSpec instanceof ICPPASTDeclSpecifier && fOptions.preserveVirtual()) { + ((ICPPASTDeclSpecifier) newDeclSpec).setVirtual(true); + } + IASTSimpleDeclaration simple = factory.newSimpleDeclaration(newDeclSpec); + if (newDeclarator instanceof ICPPASTFunctionDeclarator) { + ICPPASTFunctionDeclarator funcDeclarator = (ICPPASTFunctionDeclarator) newDeclarator; + funcDeclarator.setPureVirtual(false); + funcDeclarator.setConst(functionType.isConst()); + if (fOptions.addOverride()) { + funcDeclarator.addVirtSpecifier(((ICPPNodeFactory) factory).newVirtSpecifier(SpecifierKind.Override)); + } + for (ICPPParameter par : parameters) { + IASTDeclarator parDeclarator = declGen.createDeclaratorFromType(par.getType(), + par.getName().toCharArray()); + IASTDeclSpecifier parSpecifier = declGen.createDeclSpecFromType(par.getType()); + IASTParameterDeclaration parameter = factory.newParameterDeclaration(parSpecifier, parDeclarator); + funcDeclarator.addParameterDeclaration(parameter); + } + for (IASTPointerOperator op : declarator.getPointerOperators()) + funcDeclarator.addPointerOperator(op.copy()); + if (declarator instanceof ICPPASTFunctionDeclarator) { + ICPPASTFunctionDeclarator orig = (ICPPASTFunctionDeclarator) declarator; + funcDeclarator.setRefQualifier(orig.getRefQualifier()); + IASTTypeId[] typesId = orig.getExceptionSpecification(); + if (typesId == IASTTypeId.EMPTY_TYPEID_ARRAY) + funcDeclarator.setEmptyExceptionSpecification(); + else { + for (IASTTypeId typeId : typesId) { + funcDeclarator.addExceptionSpecificationTypeId(typeId == null ? null : typeId.copy()); + } + } + ICPPASTExpression noexceptExpression = orig.getNoexceptExpression(); + if (noexceptExpression != null) { + funcDeclarator.setNoexceptExpression( + noexceptExpression == ICPPASTFunctionDeclarator.NOEXCEPT_DEFAULT ? noexceptExpression + : (ICPPASTExpression) noexceptExpression.copy()); + } + } + } + simple.addDeclarator(newDeclarator); + simple.setDeclSpecifier(newDeclSpec); + simple.setParent(getDeclSpecifier()); + return simple; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/MethodCollector.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/MethodCollector.java new file mode 100644 index 00000000000..9866ce5f153 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/MethodCollector.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2017 Pavel Marek + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Pavel Marek - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.overridemethods; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; + +/** + * Virtual method collector invoked from {@link VirtualMethodsASTVisitor}. + * @author Pavel Marek + */ +public class MethodCollector { + /** + * Ignore virtual destructors. + * @param base + * @return + */ + private ICPPMethod[] virtualMethods(ICPPClassType clas) { + ArrayList virtualMethods = new ArrayList<>(); + + ICPPMethod[] methods = clas.getDeclaredMethods(); + // Traverse all methods and check for virtuality. + for (ICPPMethod method : methods) { + if (method.isVirtual() && !method.isDestructor() && !method.isFinal()) { + virtualMethods.add(method); + } + } + + return virtualMethods.toArray(new ICPPMethod[virtualMethods.size()]); + } + + private List getBaseClasses(ICPPClassType classType) { + List baseClasses = new ArrayList<>(); + + ICPPBase[] bases = classType.getBases(); + for (int i = 0; i < bases.length; i++) { + IBinding binding = bases[i].getBaseClass(); + if (binding instanceof ICPPClassType) { + baseClasses.add((ICPPClassType) binding); + } + } + + return baseClasses; + } + + /** + * Implemented with recursion. + * @param container + * @param classType + */ + private void fillContainerRecursion(VirtualMethodContainer container, ICPPClassType classType, + IASTDeclSpecifier declSpecifier) { + List baseClasses = getBaseClasses(classType); + // Recursion base (at top base class). + if (baseClasses.size() == 0) { + container.addMethodsToClass(classType, virtualMethods(classType), declSpecifier); + } else { + for (ICPPClassType baseClass : baseClasses) { + // Recurse. + fillContainerRecursion(container, baseClass, declSpecifier); + } + // Add also virtual methods of this class. + container.addMethodsToClass(classType, virtualMethods(classType), declSpecifier); + } + } + + /** + * Just calls private method - this is to avoid storing virtual methods from + * the current class eg. the class that the refactoring was invoked from. + * @param container + * @param classType + */ + public void fillContainer(VirtualMethodContainer container, ICPPClassType classType, + IASTDeclSpecifier declSpecifier) { + List baseClasses = getBaseClasses(classType); + // Check if there are any base classes. + if (baseClasses.size() != 0) { + for (ICPPClassType baseClass : baseClasses) { + // Recurse. + fillContainerRecursion(container, baseClass, declSpecifier); + } + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsAction.java new file mode 100644 index 00000000000..4188e9b6778 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsAction.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2017 Pavel Marek + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Pavel Marek - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.overridemethods; + +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.IWorkingCopy; +import org.eclipse.cdt.ui.refactoring.actions.RefactoringAction; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.window.IShellProvider; +import org.eclipse.ui.IEditorPart; + +/** + * @noextend This class is not intended to be subclassed by clients. + */ +public class OverrideMethodsAction extends RefactoringAction { + + public OverrideMethodsAction() { + super(Messages.OverrideMethods_label); + } + + public OverrideMethodsAction(IEditorPart editor) { + this(); + setEditor(editor); + } + + @Override + public void run(IShellProvider shellProvider, IWorkingCopy wc, ITextSelection selection) { + if (wc.getResource() != null) { + new OverrideMethodsRefactoringRunner(wc, selection, shellProvider, wc.getCProject()).run(); + } + } + + @Override + public void run(IShellProvider shellProvider, ICElement elem) { + new OverrideMethodsRefactoringRunner(elem, null, shellProvider, elem.getCProject()).run(); + } + + @Override + public void updateSelection(ICElement elem) { + super.updateSelection(elem); + if (elem != null && elem.getElementType() != ICElement.C_CLASS) { + setEnabled(false); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsInputPage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsInputPage.java new file mode 100644 index 00000000000..5de3d615ac4 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsInputPage.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2017 Pavel Marek + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Pavel Marek - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.overridemethods; + +import java.util.List; + +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; +import org.eclipse.cdt.internal.ui.preferences.CodeStylePreferencePage; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTreeViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.ltk.ui.refactoring.UserInputWizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Link; +import org.eclipse.ui.dialogs.ContainerCheckedTreeViewer; +import org.eclipse.ui.dialogs.PreferencesUtil; + +/** + * This class represents the only InputPage of the wizard for this code generation + * @author Pavel Marek + */ +public class OverrideMethodsInputPage extends UserInputWizardPage { + private OverrideMethodsRefactoring fRefactoring; + private CheckboxTreeViewer fTree; + + public OverrideMethodsInputPage(OverrideMethodsRefactoring refactoring) { + super(Messages.OverrideMethodsInputPage_Name); + this.fRefactoring = refactoring; + } + + /** + * Adds "Select All" and "Deselect All" to given Composite. + * @param parent + */ + private Composite createButtons(Composite parent) { + Composite buttonComposite = new Composite(parent, SWT.NONE); + FillLayout layout = new FillLayout(SWT.VERTICAL); + layout.spacing = 4; + buttonComposite.setLayout(layout); + + Button selAllButton = new Button(buttonComposite, SWT.PUSH); + selAllButton.setText(Messages.OverrideMethodsInputPage_SelectAll); + selAllButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + List allMethods = fRefactoring.getMethodContainer().getAllMethods(); + + // Add all methods from container to PrintData. + fTree.setCheckedElements(allMethods.toArray()); + fRefactoring.getPrintData().addMethods(allMethods); + checkPageComplete(); + } + + }); + + Button deselAllButton = new Button(buttonComposite, SWT.PUSH); + deselAllButton.setText(Messages.OverrideMethodsInputPage_DeselectAll); + deselAllButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + List allMethods = fRefactoring.getMethodContainer().getAllMethods(); + + // Uncheck all methods from tree. + for (Method method : allMethods) { + fTree.setChecked(method, false); + } + + fRefactoring.getPrintData().removeMethods(allMethods); + checkPageComplete(); + } + + }); + return buttonComposite; + } + + private void createTree(Composite parent) { + fTree = new ContainerCheckedTreeViewer(parent); + fTree.setContentProvider(fRefactoring.getMethodContainer()); + fTree.setAutoExpandLevel(3); + // Populate the tree. + fTree.setInput(fRefactoring.getMethodContainer().getInitialInput()); + // Horizontal fill. + fTree.getTree().setLayoutData(new GridData(GridData.FILL_BOTH)); + + fTree.addCheckStateListener(new ICheckStateListener() { + + @Override + public void checkStateChanged(CheckStateChangedEvent event) { + VirtualMethodPrintData printData = fRefactoring.getPrintData(); + + // ICPPClassType (parent) checked. + if (event.getElement() instanceof ICPPClassType) { + ICPPClassType parentClass = (ICPPClassType) event.getElement(); + VirtualMethodContainer methodContainer = fRefactoring.getMethodContainer(); + List selectedMethods = methodContainer.getMethods(parentClass); + + // Add (or remove) all methods that are displayed as children in the tree + // to PrintData. + if (event.getChecked()) { + printData.addMethods(selectedMethods); + } else { + printData.removeMethods(selectedMethods); + } + } else if (event.getElement() instanceof Method) { + Method selectedMethod = (Method) event.getElement(); + + if (event.getChecked()) { + printData.addMethod(selectedMethod); + } else { + printData.removeMethod(selectedMethod); + } + } + + checkPageComplete(); + } + }); + + // Set all nodes (Methods) in the tree to unchecked. + for (Method method : fRefactoring.getMethodContainer().getAllMethods()) { + fTree.setChecked(method, false); + } + } + + @Override + public void createControl(Composite parent) { + setTitle(Messages.OverrideMethodsInputPage_Name); + setMessage(Messages.OverrideMethodsInputPage_Header); + + Composite comp = new Composite(parent, SWT.NONE); + comp.setLayout(new GridLayout(2, false)); + createTree(comp); + GridData gd = new GridData(GridData.FILL_BOTH); + fTree.getTree().setLayoutData(gd); + + Composite buttonContainer = createButtons(comp); + gd = new GridData(); + gd.verticalAlignment = SWT.TOP; + buttonContainer.setLayoutData(gd); + + final Button ignoreVirtual = new Button(comp, SWT.CHECK); + gd = new GridData(); + gd.horizontalSpan = 2; + gd.heightHint = 20; + ignoreVirtual.setLayoutData(gd); + ignoreVirtual.setText(Messages.OverrideMethodsRefactoring_PreserveVirtual); + ignoreVirtual.setSelection(fRefactoring.getOptions().preserveVirtual()); + ignoreVirtual.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + fRefactoring.getOptions().setPreserveVirtual(ignoreVirtual.getSelection()); + } + }); + final Button addOverridden = new Button(comp, SWT.CHECK); + gd = new GridData(); + gd.horizontalSpan = 2; + gd.heightHint = 40; + addOverridden.setLayoutData(gd); + addOverridden.setText(Messages.OverrideMethodsRefactoring_AddOverride); + addOverridden.setSelection(fRefactoring.getOptions().addOverride()); + addOverridden.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + fRefactoring.getOptions().setAddOverride(addOverridden.getSelection()); + } + }); + Link link = new Link(comp, SWT.WRAP); + link.setText(Messages.OverrideMethodsRefactoring_LinkDescription); + link.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + PreferencesUtil.createPreferenceDialogOn(getShell(), CodeStylePreferencePage.PREF_ID, + new String[] { CodeStylePreferencePage.PREF_ID }, null).open(); + } + }); + link.setToolTipText(Messages.OverrideMethodsRefactoring_LinkTooltip); + + gd = new GridData(SWT.FILL, SWT.CENTER, true, false); + gd.grabExcessHorizontalSpace = true; + link.setLayoutData(gd); + + checkPageComplete(); + setControl(comp); + } + + /** + * Sets page complete under certain conditions. + * + * Note that if the page is complete, the "Preview" and "OK" buttons + * are enabled. + */ + private void checkPageComplete() { + if (fRefactoring.getPrintData().isEmpty()) { + setPageComplete(false); + } else { + setPageComplete(true); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsRefactoring.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsRefactoring.java new file mode 100644 index 00000000000..d899f364cdc --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsRefactoring.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2017 Pavel Marek + * Copyright (c) 2019 Marco Stornelli + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Pavel Marek - initial API and implementation + * Marco Stornelli - Improvements + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.overridemethods; + +import java.util.List; + +import org.eclipse.cdt.core.browser.TypeUtil; +import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.internal.ui.refactoring.CRefactoring; +import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector; +import org.eclipse.cdt.internal.ui.refactoring.implementmethod.ImplementMethodRefactoring; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; + +/** + * This adds "Override Methods" functionality to Source context menu + * located both in main menu and as a pop-up menu in editor. User can select + * which virtual methods overrides from which classes should be generated. + * This code generation feature is supposed to be triggered mostly in header + * files. Every method is added under corresponding visibility label, in case + * when no labels are found, they are generated in order set in preferences. + *

+ * Code of this contribution is inspired from "Generate getters and setters" + * code generation and "Extract constant" refactoring. + *

+ * + * Steps of this refactoring are: + * 1) Initial conditions checking. + * The initial conditions are satisfied when + * the selection (cursor) is located inside a class definition, this class has + * some base classes, and there is at least one virtual method to override. + * During this step the {@link VirtualMethodASTVisitor} traverses the AST for + * the current file and finds the class that is selected. The binding for this + * class is resolved and from this binding all the informations about base + * classes and their virtual methods are gathered inside {@link VirtualMethodContainer}. + * + * 2) Method selection (dialog with user). + * {@link OverrideMethodsInputPage} represents the only WizardInputPage + * that this code generation consists of. This wizard looks similar to the wizard + * from "Generate getters and setters" - there is a CheckBoxTreeView + * where parent nodes represent base classes and children nodes represent their + * virtual methods. + * When one of items (virtual methods) is checked, the corresponding method + * is saved to {@link VirtualMethodPrintData}. + * + * 3) Collection of all changes. + * This step is handled just by {@link VirtualMethodPrintData} that adds + * selected methods inside class (rewrites the corresponding AST with the help + * of {@link org.eclipse.cdt.internal.ui.refactoring.ClassMemberInserter}). + * + * @author Pavel Marek + */ +public class OverrideMethodsRefactoring extends CRefactoring { + private VirtualMethodsASTVisitor fVirtualMethodVisitor; + private OverrideOptions fOptions; + private VirtualMethodContainer fMethodContainer; + private VirtualMethodPrintData fPrintData = new VirtualMethodPrintData(); + private ImplementMethodRefactoring fImplementMethodRefactoring; + + public VirtualMethodPrintData getPrintData() { + return fPrintData; + } + + public VirtualMethodContainer getMethodContainer() { + return fMethodContainer; + } + + public OverrideMethodsRefactoring(ICElement element, ISelection selection, ICProject project) { + super(element, selection, project); + + fOptions = new OverrideOptions(project); + fMethodContainer = new VirtualMethodContainer(fOptions); + fVirtualMethodVisitor = new VirtualMethodsASTVisitor((ITextSelection) selection, tu.getFile().getName(), + fMethodContainer, TypeUtil.getFullyQualifiedName(element).getFullyQualifiedName()); + fImplementMethodRefactoring = new ImplementMethodRefactoring(element, selection, project); + } + + @Override + protected RefactoringDescriptor getRefactoringDescriptor() { + return null; + } + + /** + * Called when preview button is pushed, that means that there is at least + * one modification. + */ + @Override + protected void collectModifications(IProgressMonitor pm, ModificationCollector collector) + throws CoreException, OperationCanceledException { + List methods = fPrintData.rewriteAST(refactoringContext, collector, + fVirtualMethodVisitor); + fImplementMethodRefactoring.setContext(refactoringContext); + fImplementMethodRefactoring.collectModifications(pm, collector, methods, fPrintData.getParentOffset()); + } + + /** + * Removes already overridden methods from VirtualMethodContainer. + * This method should be called after fVirtualMethodVisitor visited the ast, + * ie. fMethodContainer was filled. + */ + private void removeOverridenMethods() { + ICPPClassType classType = fVirtualMethodVisitor.getClassBinding(); + + // Remove all methods that are declared in this class. + for (ICPPMethod method : classType.getDeclaredMethods()) { + fMethodContainer.remove(method); + } + } + + /** + * Checks whether selection is inside class. + * Also initializes fMethodContainer. + * @throws CoreException + * @throws OperationCanceledException + */ + @Override + public RefactoringStatus checkInitialConditions(IProgressMonitor pm) + throws CoreException, OperationCanceledException { + SubMonitor subMonitor = SubMonitor.convert(pm, 5); + RefactoringStatus status = super.checkInitialConditions(pm); + IASTTranslationUnit ast = getAST(tu, subMonitor.split(1)); + + // Find the class inside which has selection inside it. + fVirtualMethodVisitor.visitAst(ast); + subMonitor.worked(3); + + if (fVirtualMethodVisitor.getClassNode() == null) { + status.addFatalError(Messages.OverrideMethodsRefactoring_SelNotInClass); + return status; + } + + // Discard methods that are already overridden from fMethodContainer. + removeOverridenMethods(); + + if (fMethodContainer.isEmpty()) { + status.addFatalError(Messages.OverrideMethodsRefactoring_NoMethods); + return status; + } + + return status; + } + + @Override + protected RefactoringStatus checkFinalConditions(IProgressMonitor subProgressMonitor, + CheckConditionsContext checkContext) throws CoreException, OperationCanceledException { + RefactoringStatus result = new RefactoringStatus(); + fImplementMethodRefactoring.finalConditions(checkContext); + return result; + } + + public OverrideOptions getOptions() { + return fOptions; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsRefactoringRunner.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsRefactoringRunner.java new file mode 100644 index 00000000000..3d2e4296fad --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsRefactoringRunner.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2017 Pavel Marek + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Pavel Marek - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.overridemethods; + +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.internal.ui.refactoring.RefactoringRunner; +import org.eclipse.cdt.internal.ui.refactoring.RefactoringSaveHelper; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.window.IShellProvider; + +public class OverrideMethodsRefactoringRunner extends RefactoringRunner { + + public OverrideMethodsRefactoringRunner(ICElement element, ISelection selection, IShellProvider shellProvider, + ICProject cProject) { + super(element, selection, shellProvider, cProject); + } + + @Override + public void run() { + OverrideMethodsRefactoring refactoring = new OverrideMethodsRefactoring(element, selection, project); + OverrideMethodsWizard wizard = new OverrideMethodsWizard(refactoring); + run(wizard, refactoring, RefactoringSaveHelper.SAVE_REFACTORING); + } + +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsWizard.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsWizard.java new file mode 100644 index 00000000000..5e57149afdb --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsWizard.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2017 Pavel Marek + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Pavel Marek - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.overridemethods; + +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.ltk.ui.refactoring.RefactoringWizard; + +public class OverrideMethodsWizard extends RefactoringWizard { + private OverrideMethodsRefactoring refactoring; + + public OverrideMethodsWizard(OverrideMethodsRefactoring refactoring) { + super(refactoring, DIALOG_BASED_USER_INTERFACE | PREVIEW_EXPAND_FIRST_NODE); + this.refactoring = refactoring; + setDefaultPageTitle(Messages.OverrideMethodsInputPage_Name); + setDialogSettings(CUIPlugin.getDefault().getDialogSettings()); + } + + @Override + protected void addUserInputPages() { + addPage(new OverrideMethodsInputPage(refactoring)); + } + +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideOptions.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideOptions.java new file mode 100644 index 00000000000..9effe2e4604 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideOptions.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2019 Marco Stornelli + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Marco Stornelli - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.overridemethods; + +import org.eclipse.cdt.core.CCorePreferenceConstants; +import org.eclipse.cdt.core.model.ICProject; + +public class OverrideOptions { + + private boolean fpreserveVirtual; + private boolean fAddOverride; + + public OverrideOptions(ICProject project) { + fAddOverride = CCorePreferenceConstants.getPreference(CCorePreferenceConstants.ADD_OVERRIDE_KEYWORD, project, + CCorePreferenceConstants.DEFAULT_ADD_OVERRIDE_KEYWORD); + fpreserveVirtual = CCorePreferenceConstants.getPreference(CCorePreferenceConstants.PRESERVE_VIRTUAL_KEYWORD, + project, CCorePreferenceConstants.DEFAULT_PRESERVE_VIRTUAL_KEYWORD); + } + + public boolean preserveVirtual() { + return fpreserveVirtual; + } + + public void setPreserveVirtual(boolean preserveVirtual) { + this.fpreserveVirtual = preserveVirtual; + } + + public boolean addOverride() { + return fAddOverride; + } + + public void setAddOverride(boolean addOverride) { + this.fAddOverride = addOverride; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodContainer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodContainer.java new file mode 100644 index 00000000000..35a8324d261 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodContainer.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * Copyright (c) 2017 Pavel Marek + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Pavel Marek - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.overridemethods; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; +import org.eclipse.jface.viewers.ITreeContentProvider; + +/** + * Container for virtual methods collected by {@link VirtualMethodsASTVisitor}. + * Also serves as content provider for CheckBoxTree in wizard. + */ +public class VirtualMethodContainer implements ITreeContentProvider { + private Map> fData = new HashMap<>(); + final private OverrideOptions fOptions; + + public VirtualMethodContainer(OverrideOptions options) { + fOptions = options; + } + + /** + * Returns all parents (ICPPClassTypes). + * @param inputElement root element. + */ + @Override + public Object[] getElements(Object inputElement) { + if (!(inputElement instanceof Map)) { + return null; + } else { + return fData.keySet().toArray(new ICPPClassType[fData.keySet().size()]); + } + } + + @Override + public Object[] getChildren(Object parentElement) { + return fData.get(parentElement).toArray(); + } + + /** + * Returns all virtual methods for given ICPPClassType. + * @param classType + */ + public List getMethods(ICPPClassType classType) { + return fData.get(classType); + } + + @Override + public Object getParent(Object element) { + for (Entry> entry : fData.entrySet()) { + if (entry.getValue().contains(element)) { + return entry.getKey(); + } + } + return null; + } + + @Override + public boolean hasChildren(Object element) { + List list = fData.get(element); + if (list == null) { + return false; + } else { + return !list.isEmpty(); + } + } + + public List getAllMethods() { + List allMethods = new ArrayList<>(); + + for (Entry> entry : fData.entrySet()) { + allMethods.addAll(entry.getValue()); + } + + return allMethods; + } + + /** + * This method is called to populate the tree viewer input, thats why + * it is not named "getData". + */ + public Map> getInitialInput() { + return fData; + } + + /** + * Checks if given method is already contained in fData. + * @param method + * @return + */ + private boolean isDuplicate(Method method) { + for (Entry> entry : fData.entrySet()) { + if (entry.getValue().contains(method)) { + return true; + } + } + return false; + } + + /** + * Adds one method to a specified ICPPClassType. + * @param classType + * @param method + */ + private void addMethodToClass(ICPPClassType classType, Method method) { + if (!isDuplicate(method)) { + List methods = fData.get(classType); + if (methods == null) { + methods = new ArrayList<>(); + fData.put(classType, methods); + } + methods.add(method); + } + } + + public void addMethodsToClass(ICPPClassType classType, ICPPMethod[] methods, IASTDeclSpecifier declSpecifier) { + for (ICPPMethod icppMethod : methods) { + addMethodToClass(classType, new Method(icppMethod, declSpecifier, fOptions)); + } + } + + public boolean isEmpty() { + return fData.isEmpty(); + } + + public void remove(ICPPMethod method) { + // Search through all saved methods. + for (Map.Entry> entry : fData.entrySet()) { + List methods = entry.getValue(); + + if (methods.remove(new Method(method, fOptions))) { + // Check if classType (parent) is empty ie. if there are no + // methods to display. + if (methods.isEmpty()) { + fData.remove(entry.getKey()); + } + break; + } + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodPrintData.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodPrintData.java new file mode 100644 index 00000000000..9775d671421 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodPrintData.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2017 Pavel Marek + * Copyright (c) 2019 Marco Stornelli + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Pavel Marek - initial API and implementation + * Marco Stornelli - Improvements + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.overridemethods; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel; +import org.eclipse.cdt.internal.ui.refactoring.CRefactoringContext; +import org.eclipse.cdt.internal.ui.refactoring.ClassMemberInserter; +import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector; +import org.eclipse.cdt.internal.ui.refactoring.utils.VisibilityEnum; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.OperationCanceledException; + +/** + * Holds virtual member functions that should be printed (during + * VirtualMethodRefactoring.collectModifications() ). + * @author mayfa + * + */ +public class VirtualMethodPrintData { + private Set privateMethods = new HashSet<>(); + private Set protectedMethods = new HashSet<>(); + private Set publicMethods = new HashSet<>(); + + /** + * Adds one method to print. + * @param method + */ + public void addMethod(Method method) { + switch (method.getMethod().getVisibility()) { + case ICPPASTVisibilityLabel.v_public: + publicMethods.add(method); + break; + case ICPPASTVisibilityLabel.v_protected: + protectedMethods.add(method); + break; + case ICPPASTVisibilityLabel.v_private: + privateMethods.add(method); + break; + } + } + + /** + * This method is called when ICPPClassType (parent) is selected in the + * tree and all its children are passed to this method. + * @param selectedMethods + */ + public void addMethods(List selectedMethods) { + for (Method method : selectedMethods) { + addMethod(method); + } + } + + /** + * Removes one method from (further) printing. + * @param method + */ + public void removeMethod(Method method) { + switch (method.getMethod().getVisibility()) { + case ICPPASTVisibilityLabel.v_public: + publicMethods.remove(method); + break; + case ICPPASTVisibilityLabel.v_protected: + protectedMethods.remove(method); + break; + case ICPPASTVisibilityLabel.v_private: + privateMethods.remove(method); + break; + } + } + + public void removeMethods(List selectedMethods) { + for (Method method : selectedMethods) { + removeMethod(method); + } + } + + public boolean isEmpty() { + return privateMethods.isEmpty() && protectedMethods.isEmpty() && publicMethods.isEmpty(); + } + + /** + * Parses all given methods. + * @param methods + * @return + * @throws CoreException + * @throws OperationCanceledException + */ + private List parseAllMethods(CRefactoringContext context, Set methods) + throws OperationCanceledException, CoreException { + List methodNodes = new ArrayList<>(); + + for (Method method : methods) { + IASTNode n = method.createNode(context); + if (n != null) + methodNodes.add(n); + } + + return methodNodes; + } + + /** + * Get the parent offset. Since every method has a reference to the + * same declaration specifier AST node, we can get the value just from + * the first one. + * @param s The set of methods + * @return The parent node offset + */ + private static int getParentOffset(Set s) { + if (!s.isEmpty()) { + Method m = s.iterator().next(); + return m.getDeclSpecifier().getFileLocation().getNodeOffset(); + } + return -1; + } + + /** + * It gets the node offset of the parent, i.e. the class which contains + * the methods. + * @return Negative number if offset can't be found, >= 0 otherwise + */ + public int getParentOffset() { + int res = getParentOffset(publicMethods); + if (res >= 0) + return res; + res = getParentOffset(protectedMethods); + if (res >= 0) + return res; + res = getParentOffset(privateMethods); + return res; + } + + /** + * Rewrites all the changes. + * @throws CoreException + * @throws OperationCanceledException + */ + public List rewriteAST(CRefactoringContext context, ModificationCollector collector, + VirtualMethodsASTVisitor visitor) throws OperationCanceledException, CoreException { + ICPPASTCompositeTypeSpecifier classNode = (ICPPASTCompositeTypeSpecifier) visitor.getClassNode(); + List methodNodes = new ArrayList<>(); + List result = new ArrayList<>(); + + methodNodes = parseAllMethods(context, publicMethods); + if (!methodNodes.isEmpty()) { + result.addAll(methodNodes.stream().map(e -> (IASTSimpleDeclaration) e).collect(Collectors.toList())); + // Add all public methods to classNode. + ClassMemberInserter.createChange(classNode, VisibilityEnum.v_public, methodNodes, true, collector); + } + + methodNodes = parseAllMethods(context, protectedMethods); + if (!methodNodes.isEmpty()) { + result.addAll(methodNodes.stream().map(e -> (IASTSimpleDeclaration) e).collect(Collectors.toList())); + // Add all protected methods to classNode. + ClassMemberInserter.createChange(classNode, VisibilityEnum.v_protected, methodNodes, true, collector); + } + + methodNodes = parseAllMethods(context, privateMethods); + if (!methodNodes.isEmpty()) { + result.addAll(methodNodes.stream().map(e -> (IASTSimpleDeclaration) e).collect(Collectors.toList())); + // Add all private methods to classNode. + ClassMemberInserter.createChange(classNode, VisibilityEnum.v_private, methodNodes, true, collector); + } + return result; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodsASTVisitor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodsASTVisitor.java new file mode 100644 index 00000000000..3e03afb135f --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodsASTVisitor.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2017 Pavel Marek + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Pavel Marek - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.overridemethods; + +import org.eclipse.cdt.core.browser.IQualifiedTypeName; +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.DOMException; +import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.IASTFileLocation; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.jface.text.ITextSelection; + +/** + * Visits the class definition inside which the selection (cursor) is located. + * Gets binding for this class, and from this binding all other necessary + * informations are gathered inside fMethodContainer. + */ +public class VirtualMethodsASTVisitor extends ASTVisitor { + private ITextSelection fSelection; + private String fFileName; + private VirtualMethodContainer fMethodContainer; + private IASTNode fClassNode; + private String fClassName; + private ICPPClassType fClassBinding; + private String fElementName; + + /** + * + * @param textSelection + * @param fileName + * @param methodContainer the VirtualMethodContainer to be filled by this + * visitor. + */ + public VirtualMethodsASTVisitor(ITextSelection textSelection, String fileName, + VirtualMethodContainer methodContainer, String elName) { + // Visit only decl specifier. + shouldVisitDeclSpecifiers = true; + + this.fClassNode = null; + this.fSelection = textSelection; + this.fFileName = fileName; + this.fMethodContainer = methodContainer; + this.fElementName = elName; + } + + /** + * + */ + public VirtualMethodContainer getVirtualMethodContainer() { + return fMethodContainer; + } + + /** + * Returns class node encapsulating text selection. + * @return null if no class was encountered, IASTNode otherwise. + */ + public IASTNode getClassNode() { + return fClassNode; + } + + public String getClassName() { + return fClassName; + } + + public ICPPClassType getClassBinding() { + return fClassBinding; + } + + /** + * Check if node is enclosing text selection. + * @param node + * @return + */ + private boolean isInsideSelection(ICPPASTCompositeTypeSpecifier node) { + IASTFileLocation location = node.getFileLocation(); + + // node has no location if it is for example built-in macro + if (location == null) { + return false; + } + + if (fSelection == null) { + IBinding binding = node.getName().resolveBinding(); + if (!(binding instanceof ICPPBinding)) + return false; + try { + String elName = String.join(IQualifiedTypeName.QUALIFIER, ((ICPPBinding) binding).getQualifiedName()); + if (elName.equals(fElementName)) + return true; + } catch (DOMException e) { + CUIPlugin.log(e); + } + return false; + } + + if (location.getNodeOffset() <= fSelection.getOffset() + && fSelection.getOffset() <= location.getNodeOffset() + location.getNodeLength() + && location.getFileName().contains(fFileName)) { + return true; + } else { + return false; + } + } + + public void visitAst(IASTTranslationUnit ast) { + ast.accept(this); + if (fClassNode != null) { + MethodCollector methodCollector = new MethodCollector(); + methodCollector.fillContainer(fMethodContainer, fClassBinding, (IASTDeclSpecifier) fClassNode); + } + } + + @Override + public int visit(IASTDeclSpecifier declSpecifier) { + // In a class or struct + if (declSpecifier instanceof ICPPASTCompositeTypeSpecifier) { + /* + * Check if this class is enclosing text selection and go ahead, + * we could hit the selection but there's a nested class so we need + * to process the most inner node with selection at the end of tree visit. + */ + if (isInsideSelection((ICPPASTCompositeTypeSpecifier) declSpecifier)) { + + // Store. + fClassNode = declSpecifier; + + // Get binding. + ICPPASTCompositeTypeSpecifier typeSpec = (ICPPASTCompositeTypeSpecifier) declSpecifier; + IBinding binding = typeSpec.getName().getBinding(); + + // Check if the binding is of class type. + if (!(binding instanceof ICPPClassType)) { + fClassNode = null; + return PROCESS_CONTINUE; + } + // Store class namegetRecursivelyAllBases + fClassName = binding.getName(); + + ICPPClassType classType = (ICPPClassType) binding; + fClassBinding = classType; + } + } + return PROCESS_CONTINUE; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java index 933b70e474b..13df14dc81c 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java @@ -29,6 +29,7 @@ import org.eclipse.cdt.internal.ui.editor.CEditor; import org.eclipse.cdt.internal.ui.editor.ICEditorActionDefinitionIds; import org.eclipse.cdt.internal.ui.editor.OrganizeIncludesAction; import org.eclipse.cdt.internal.ui.editor.SortLinesAction; +import org.eclipse.cdt.internal.ui.refactoring.overridemethods.OverrideMethodsAction; import org.eclipse.cdt.ui.refactoring.actions.GettersAndSettersAction; import org.eclipse.cdt.ui.refactoring.actions.ImplementMethodAction; import org.eclipse.cdt.ui.refactoring.actions.RefactoringAction; @@ -118,7 +119,7 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange private List fRefactorActions = new ArrayList<>(); private AddIncludeAction fAddInclude; - // private OverrideMethodsAction fOverrideMethods; + private OverrideMethodsAction fOverrideMethods; // private GenerateHashCodeEqualsAction fHashCodeEquals; private GettersAndSettersAction fAddGetterSetter; private ImplementMethodAction fImplementMethod; @@ -178,9 +179,9 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange editor.setAction("CopyQualifiedName", fCopyQualifiedNameAction); //$NON-NLS-1$ editor.markAsSelectionDependentAction("CopyQualifiedName", true); //$NON-NLS-1$ // - // fOverrideMethods= new OverrideMethodsAction(editor); - // fOverrideMethods.setActionDefinitionId(ICEditorActionDefinitionIds.OVERRIDE_METHODS); - // editor.setAction("OverrideMethods", fOverrideMethods); //$NON-NLS-1$ + fOverrideMethods = new OverrideMethodsAction(editor); + fOverrideMethods.setActionDefinitionId(ICEditorActionDefinitionIds.OVERRIDE_METHODS); + editor.setAction("OverrideMethods", fOverrideMethods); //$NON-NLS-1$ // fAddGetterSetter = new GettersAndSettersAction(editor); fAddGetterSetter.setActionDefinitionId(ICEditorActionDefinitionIds.GETTERS_AND_SETTERS); @@ -263,9 +264,11 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange fSelectionProvider = selectionProvider == null ? fSite.getSelectionProvider() : selectionProvider; ISelection selection = fSelectionProvider.getSelection(); - // fOverrideMethods= new OverrideMethodsAction(site); - // fOverrideMethods.setActionDefinitionId(ICEditorActionDefinitionIds.OVERRIDE_METHODS); - // + fOverrideMethods = new OverrideMethodsAction(); + fOverrideMethods.setActionDefinitionId(ICEditorActionDefinitionIds.OVERRIDE_METHODS); + fOverrideMethods.setSite(fSite); + fRefactorActions.add(fOverrideMethods); + fAddGetterSetter = new GettersAndSettersAction(); fAddGetterSetter.setActionDefinitionId(ICEditorActionDefinitionIds.GETTERS_AND_SETTERS); fAddGetterSetter.setSite(fSite); @@ -314,7 +317,6 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange // fCleanUp= new CleanUpAction(site); // fCleanUp.setActionDefinitionId(ICEditorActionDefinitionIds.CLEAN_UP); - // fOverrideMethods.update(selection); // fAddDelegateMethods.update(selection); // fAddUnimplementedConstructors.update(selection); // fGenerateConstructorUsingFields.update(selection); @@ -335,7 +337,6 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange fAddTaskAction.setEnabled(false); } - // registerSelectionListener(fSelectionProvider, fOverrideMethods); // registerSelectionListener(fSelectionProvider, fAddDelegateMethods); // registerSelectionListener(fSelectionProvider, fAddUnimplementedConstructors); // registerSelectionListener(fSelectionProvider, fGenerateConstructorUsingFields); @@ -442,7 +443,7 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange added += addAction(source, fSortLines); // added+= addAction(source, fCleanUp); source.add(new Separator(GROUP_GENERATE)); - // added+= addAction(source, fOverrideMethods); + added += addAction(source, fOverrideMethods); added += addAction(source, fAddGetterSetter); added += addAction(source, fImplementMethod); // added+= addAction(source, fAddDelegateMethods); @@ -468,7 +469,7 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange // added+= addAction(source, fSortMembers); // added+= addAction(source, fCleanUp); source.add(new Separator(GROUP_GENERATE)); - // added+= addAction(source, fOverrideMethods); + added += addAction(source, fOverrideMethods); added += addAction(source, fAddGetterSetter); added += addAction(source, fImplementMethod); // added+= addAction(source, fAddDelegateMethods); @@ -503,7 +504,7 @@ public class GenerateActionGroup extends ActionGroup implements ISelectionChange private void setGlobalActionHandlers(IActionBars actionBar) { actionBar.setGlobalActionHandler(CdtActionConstants.ADD_INCLUDE, fAddInclude); - // actionBar.setGlobalActionHandler(CdtActionConstants.OVERRIDE_METHODS, fOverrideMethods); + actionBar.setGlobalActionHandler(CdtActionConstants.OVERRIDE_METHODS, fOverrideMethods); actionBar.setGlobalActionHandler(CdtActionConstants.GETTERS_AND_SETTERS, fAddGetterSetter); actionBar.setGlobalActionHandler(CdtActionConstants.IMPLEMENT_METHOD, fImplementMethod); // actionBar.setGlobalActionHandler(CdtActionConstants.GENERATE_DELEGATE_METHODS, fAddDelegateMethods); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/Messages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/Messages.java index 6181eb35546..f42058a7cb2 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/Messages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/Messages.java @@ -25,6 +25,7 @@ class Messages extends NLS { public static String ImplementMethodAction_label; public static String GettersAndSetters_label; public static String ToggleFunctionAction_label; + public static String OverrideMethods_label; static { NLS.initializeMessages(Messages.class.getName(), Messages.class); -- cgit v1.2.3