mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-21 21:52:10 +02:00
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>
This commit is contained in:
parent
5edac6e20c
commit
6be494466b
29 changed files with 2223 additions and 25 deletions
|
@ -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
|
||||
|
|
|
@ -368,6 +368,36 @@ public class CCorePreferenceConstants {
|
|||
*/
|
||||
public static final boolean DEFAULT_PLACE_CONST_RIGHT_OF_TYPE = false;
|
||||
|
||||
/**
|
||||
* A named preference that specifies whether the override keyword should be added
|
||||
* to method signature.
|
||||
*
|
||||
* @since 6.8
|
||||
*/
|
||||
public static final String ADD_OVERRIDE_KEYWORD = "astwriter.addOverride"; //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* A named preference that specifies whether the virtual keyword should be added
|
||||
* to method signature.
|
||||
*
|
||||
* @since 6.8
|
||||
*/
|
||||
public static final String PRESERVE_VIRTUAL_KEYWORD = "astwriter.preserveVirtual"; //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* Default value for {@link #ADD_OVERRIDE_KEYWORD}.
|
||||
*
|
||||
* @since 6.8
|
||||
*/
|
||||
public static final boolean DEFAULT_ADD_OVERRIDE_KEYWORD = false;
|
||||
|
||||
/**
|
||||
* Default value for {@link #PRESERVE_VIRTUAL_KEYWORD}.
|
||||
*
|
||||
* @since 6.8
|
||||
*/
|
||||
public static final boolean DEFAULT_PRESERVE_VIRTUAL_KEYWORD = true;
|
||||
|
||||
/**
|
||||
* Returns the node in the preference in the given context.
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
@ -3237,6 +3244,11 @@
|
|||
description="%ActionDefinition.gettersAndSetters.description"
|
||||
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"
|
||||
|
|
|
@ -111,6 +111,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>).
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
}
|
|
@ -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...
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue