1
0
Fork 0
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:
Marco Stornelli 2019-03-09 10:22:10 +01:00
parent 5edac6e20c
commit 6be494466b
29 changed files with 2223 additions and 25 deletions

View file

@ -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

View file

@ -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.
*

View file

@ -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);
}
}

View file

@ -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 {

View file

@ -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();
}
}

View file

@ -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",

View file

@ -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

View file

@ -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"

View file

@ -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>).

View file

@ -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) {
}

View file

@ -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;

View file

@ -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

View file

@ -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();

View file

@ -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()) {

View file

@ -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() {
}
}

View file

@ -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...

View file

@ -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;
}
}

View file

@ -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);
}
}
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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));
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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);