Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarco Stornelli2019-03-09 04:22:10 -0500
committerMarco Stornelli2019-04-28 02:12:04 -0400
commit6be494466b397670a0c21842dc3a66d1ad6d015d (patch)
treefa029eeb6141960efe5348fac7dc980934f78a31
parent5edac6e20c69845546fe873268989f53b5f55a48 (diff)
downloadorg.eclipse.cdt-6be494466b397670a0c21842dc3a66d1ad6d015d.tar.gz
org.eclipse.cdt-6be494466b397670a0c21842dc3a66d1ad6d015d.tar.xz
org.eclipse.cdt-6be494466b397670a0c21842dc3a66d1ad6d015d.zip
Bug 303870 - Add override virtual methods functionality
Added overridemethods package Change-Id: I73a8f0a396336acf7d3bbc8988e629da510ae781 Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com> Signed-off-by: pmarek <pavel.akira.marek@gmail.com>
-rw-r--r--core/org.eclipse.cdt.core/META-INF/MANIFEST.MF2
-rw-r--r--core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/CCorePreferenceConstants.java30
-rw-r--r--core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/CCorePreferenceInitializer.java4
-rw-r--r--core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java3
-rwxr-xr-xcore/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/overridemethods/OverrideMethodsRefactoringTest.java606
-rw-r--r--core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF1
-rw-r--r--core/org.eclipse.cdt.ui/plugin.properties3
-rw-r--r--core/org.eclipse.cdt.ui/plugin.xml12
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java6
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeStyleBlock.java21
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java3
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties3
-rwxr-xr-xcore/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoring.java87
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/MethodDefinitionInsertLocationFinder.java6
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Messages.java38
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Messages.properties22
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/Method.java197
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/MethodCollector.java101
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsAction.java56
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsInputPage.java218
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsRefactoring.java179
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsRefactoringRunner.java37
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideMethodsWizard.java34
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/OverrideOptions.java46
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodContainer.java158
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodPrintData.java187
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/overridemethods/VirtualMethodsASTVisitor.java162
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/GenerateActionGroup.java25
-rw-r--r--core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/Messages.java1
29 files changed, 2223 insertions, 25 deletions
diff --git a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF
index d652f2cf9e..e6bc529ced 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 715f8709fa..72e56a97fe 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
@@ -369,6 +369,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.
*
* @param key The preference key.
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 f76419e5a7..08be850207 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 5c4c250369..c8a5731f2a 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 0000000000..a805f0346d
--- /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<ICPPClassType, List<Method>> map = refactoring.getMethodContainer().getInitialInput();
+ for (Map.Entry<ICPPClassType, List<Method>> entry : map.entrySet()) {
+ List<Method> 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 T>
+ //class X: public Base {
+ //public:
+ // X();
+ // /*$*//*$$*/
+ //};
+ //====================
+ //class Base {
+ //public:
+ // virtual ~Base();
+ // virtual void baseFunc() const = 0;
+ //};
+ //template<class T>
+ //class X: public Base {
+ //public:
+ // X();
+ // virtual void baseFunc() const;
+ //};
+ //
+ //template<class T>
+ //inline void X<T>::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 689e852e66..f527ef584f 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 1ca78dfdc6..9aac275b99 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 802941373b..909d260636 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">
</action>
+ <action
+ definitionId="org.eclipse.cdt.ui.refactor.override.methods"
+ label="%Refactoring.overrideMethods.label"
+ menubarPath="org.eclipse.jdt.ui.source.menu/generateGroup"
+ id="org.eclipse.cdt.ui.actions.OverrideMethods"
+ retarget="true">
+ </action>
<!-- Import Group -->
<action
definitionId="org.eclipse.cdt.ui.edit.text.c.sort.lines"
@@ -3238,6 +3245,11 @@
categoryId="org.eclipse.cdt.ui.category.source"
id="org.eclipse.cdt.ui.refactor.getters.and.setters"/>
<command
+ name="%ActionDefinition.overrideMethods.name"
+ description="%ActionDefinition.overrideMethods.description"
+ categoryId="org.eclipse.cdt.ui.category.source"
+ id="org.eclipse.cdt.ui.refactor.override.methods"/>
+ <command
name="%ActionDefinition.surroundWith.quickMenu.name"
description="%ActionDefinition.surroundWith.quickMenu.description"
categoryId="org.eclipse.cdt.ui.category.source"
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java
index 81d8b31c12..71d8655bfe 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java
@@ -112,6 +112,12 @@ public interface ICEditorActionDefinitionIds extends ITextEditorActionDefinition
public static final String EXTRACT_CONSTANT = "org.eclipse.cdt.ui.refactor.extract.constant"; //$NON-NLS-1$
/**
+ * Action definition ID of the refactor -> override methods action
+ * (value <code>"org.eclipse.cdt.ui.refactor.override.methods"</code>).
+ */
+ 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 <code>"org.eclipse.cdt.ui.refactor.extract.local.variable"</code>).
*/
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 170351745a..d702098eb1 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 fcb869f507..4298052a36 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 1e56f82004..7a42b56767 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 6574bb089a..5d60ae6252 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<MethodToImplementConfig> 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<IASTSimpleDeclaration> methods, int functionOffset) throws CoreException, OperationCanceledException {
+ data.setMethodDeclarations(methods);
+ for (MethodToImplementConfig config : data.getMethodDeclarations()) {
+ config.setChecked(true);
+ }
+ List<MethodToImplementConfig> 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 9261859b49..88ad130e39 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<IASTSimpleDeclaration> getAllFollowingSimpleDeclarationsFromClass(
IASTDeclaration[] declarations, IASTFileLocation methodPosition, IProgressMonitor pm) {
ArrayList<IASTSimpleDeclaration> 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 0000000000..45d6ba6c3d
--- /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 0000000000..6d8f26aa6e
--- /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 <a>Code Style</a> 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 0000000000..7da8987a12
--- /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 0000000000..9866ce5f15
--- /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<ICPPMethod> 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<ICPPClassType> getBaseClasses(ICPPClassType classType) {
+ List<ICPPClassType> 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<ICPPClassType> 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<ICPPClassType> 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 0000000000..4188e9b677
--- /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 0000000000..5de3d615ac
--- /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<Method> 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<Method> 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<Method> 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 0000000000..d899f364cd
--- /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.
+ * <p>
+ * Code of this contribution is inspired from "Generate getters and setters"
+ * code generation and "Extract constant" refactoring.
+ * </p>
+ *
+ * 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 <code>WizardInputPage</code>
+ * that this code generation consists of. This wizard looks similar to the wizard
+ * from "Generate getters and setters" - there is a <code>CheckBoxTreeView</code>
+ * 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<IASTSimpleDeclaration> 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 0000000000..3d2e4296fa
--- /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 0000000000..5e57149afd
--- /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 0000000000..9effe2e460
--- /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 0000000000..35a8324d26
--- /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 <code>CheckBoxTree</code> in wizard.
+ */
+public class VirtualMethodContainer implements ITreeContentProvider {
+ private Map<ICPPClassType, List<Method>> 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<Method> getMethods(ICPPClassType classType) {
+ return fData.get(classType);
+ }
+
+ @Override
+ public Object getParent(Object element) {
+ for (Entry<ICPPClassType, List<Method>> entry : fData.entrySet()) {
+ if (entry.getValue().contains(element)) {
+ return entry.getKey();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean hasChildren(Object element) {
+ List<Method> list = fData.get(element);
+ if (list == null) {
+ return false;
+ } else {
+ return !list.isEmpty();
+ }
+ }
+
+ public List<Method> getAllMethods() {
+ List<Method> allMethods = new ArrayList<>();
+
+ for (Entry<ICPPClassType, List<Method>> 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<ICPPClassType, List<Method>> getInitialInput() {
+ return fData;
+ }
+
+ /**
+ * Checks if given method is already contained in fData.
+ * @param method
+ * @return
+ */
+ private boolean isDuplicate(Method method) {
+ for (Entry<ICPPClassType, List<Method>> 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<Method> 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<ICPPClassType, List<Method>> entry : fData.entrySet()) {
+ List<Method> 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 0000000000..9775d67142
--- /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
+ * <code> VirtualMethodRefactoring.collectModifications() </code>).
+ * @author mayfa
+ *
+ */
+public class VirtualMethodPrintData {
+ private Set<Method> privateMethods = new HashSet<>();
+ private Set<Method> protectedMethods = new HashSet<>();
+ private Set<Method> 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<Method> 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<Method> 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<IASTNode> parseAllMethods(CRefactoringContext context, Set<Method> methods)
+ throws OperationCanceledException, CoreException {
+ List<IASTNode> 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<Method> 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<IASTSimpleDeclaration> rewriteAST(CRefactoringContext context, ModificationCollector collector,
+ VirtualMethodsASTVisitor visitor) throws OperationCanceledException, CoreException {
+ ICPPASTCompositeTypeSpecifier classNode = (ICPPASTCompositeTypeSpecifier) visitor.getClassNode();
+ List<IASTNode> methodNodes = new ArrayList<>();
+ List<IASTSimpleDeclaration> 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 0000000000..3e03afb135
--- /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 933b70e474..13df14dc81 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<RefactoringAction> 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 6181eb3554..f42058a7cb 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);

Back to the top