From 6df6c40158c211d18f71da8d5380e68894ff0d2f Mon Sep 17 00:00:00 2001 From: Markus Schorn Date: Wed, 16 Apr 2008 13:09:08 +0000 Subject: [PATCH] Implement Method (work in progress) by Emanuel Graf, bug 226646. --- .../changegenerator/ChangeGenerator.java | 7 +- .../resources/refactoring/ImplementMethod.rts | 582 ++++++++++++++++++ .../refactoring/RefactoringTestSuite.java | 2 +- .../ImplementMethodRefactoringTest.java | 71 +++ .../utils/PseudoNameGeneratorTest.java | 63 ++ .../refactoring/utils/UtilTestSuite.java | 1 + core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF | 1 + core/org.eclipse.cdt.ui/plugin.properties | 3 + core/org.eclipse.cdt.ui/plugin.xml | 13 + .../editor/ICEditorActionDefinitionIds.java | 6 + .../ui/refactoring/dialogs/Messages.java | 3 + .../dialogs/ValidatingLabeledTextField.java | 198 ++++++ .../refactoring/dialogs/messages.properties | 3 + .../ImplementMethodRefactoring.java | 248 ++++++++ .../ImplementMethodRefactoringRunner.java | 55 ++ .../ImplementMethodRefactoringWizard.java | 35 ++ .../implementmethod/InsertLocation.java | 89 +++ .../refactoring/implementmethod/Messages.java | 32 + .../MethodDefinitionInsertLocationFinder.java | 133 ++++ .../ParameterNamesInputPage.java | 101 +++ .../implementmethod/messages.properties | 15 + .../refactoring/utils/DefinitionFinder.java | 109 ++++ .../ui/refactoring/utils/NameHelper.java | 67 ++ .../ui/refactoring/utils/NamespaceHelper.java | 108 ++++ .../ui/refactoring/utils/NodeHelper.java | 63 ++ .../utils/PseudoNameGenerator.java | 48 ++ .../ui/refactoring/utils/SelectionHelper.java | 51 ++ .../cdt/ui/actions/CdtActionConstants.java | 6 + .../actions/CRefactoringActionGroup.java | 7 + .../actions/ImplementMethodAction.java | 61 ++ .../cdt/ui/refactoring/actions/Messages.java | 1 + .../refactoring/actions/messages.properties | 1 + 32 files changed, 2180 insertions(+), 3 deletions(-) create mode 100644 core/org.eclipse.cdt.ui.tests/resources/refactoring/ImplementMethod.rts create mode 100644 core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/implementmethod/ImplementMethodRefactoringTest.java create mode 100644 core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/PseudoNameGeneratorTest.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/dialogs/ValidatingLabeledTextField.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoring.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoringRunner.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoringWizard.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/InsertLocation.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/Messages.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/MethodDefinitionInsertLocationFinder.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ParameterNamesInputPage.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/messages.properties create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/DefinitionFinder.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/NameHelper.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/NamespaceHelper.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/NodeHelper.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/PseudoNameGenerator.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/SelectionHelper.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/ImplementMethodAction.java diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java index 4129be85926..b7b757843d5 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/changegenerator/ChangeGenerator.java @@ -129,8 +129,11 @@ public class ChangeGenerator extends CPPASTVisitor { synthTreatment(translationUnit); } - sourceOffsets.put(translationUnit.getFileLocation().getFileName(), - Integer.valueOf(translationUnit.getFileLocation().getNodeOffset())); + final IASTFileLocation fileLocation = translationUnit.getFileLocation(); + if (fileLocation != null) { + sourceOffsets.put(fileLocation.getFileName(), + Integer.valueOf(fileLocation.getNodeOffset())); + } return super.visit(translationUnit); } diff --git a/core/org.eclipse.cdt.ui.tests/resources/refactoring/ImplementMethod.rts b/core/org.eclipse.cdt.ui.tests/resources/refactoring/ImplementMethod.rts new file mode 100644 index 00000000000..7d4b3ce4e0d --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/resources/refactoring/ImplementMethod.rts @@ -0,0 +1,582 @@ +//!implement in existing namespace +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +namespace NameSpace +{ + class ClassInNamespace + { + public: + int test(); + //$void test2();$// + }; +} + +//@A.cpp +#include "A.h" + +namespace NameSpace +{ + int ClassInNamespace::test() + { + return 5; + } +} +//= +#include "A.h" + +namespace NameSpace +{ + int ClassInNamespace::test() + { + return 5; + } + + void ClassInNamespace::test2() + { + + } +} +//!method declared in otherwise empty class +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +class A +{ +public: + //$void test();$// +}; + +//= +class A +{ +public: + void test(); +}; + +//@A.cpp + +//= + +void A::test() +{ + +} + +//!virtual method in the middle of con/destructor, without parameters and void return value +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +#ifndef A_H_ +#define A_H_ + +class A +{ +public: + A(); + //$virtual void foo();$// + ~A(); +}; + +#endif /*A_H_*/ +//@A.cpp +#include "A.h" + +A::A() +{ +} + +A::~A() +{ +} + +//= +#include "A.h" + +A::A() +{ +} + +void A::foo() +{ +} + +A::~A() +{ +} + +//!implement a function at start of source file +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +//$void function();$// +void function_with_impl(); +//@A.cpp +void function_with_impl() +{ +} +//= +void function() +{ +} + +void function_with_impl() +{ +} +//!method declared in otherwise empty class without cpp file +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +class A +{ +public: + //$void test();$// +}; + +//= +class A +{ +public: + void test(); +}; + +inline void A::test() +{ + +} + +//!method at end, without parameters and void return value +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +#ifndef A_H_ +#define A_H_ + +class A +{ +public: + A(); + //$void foo();$// +}; + +#endif /*A_H_*/ +//@A.cpp +#include "A.h" + +A::A() +{ +} + +//= +#include "A.h" + +A::A() +{ +} + +void A::foo() +{ + +} + +//!method at beginning, without parameters, void return value and const +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +#ifndef A_H_ +#define A_H_ + +class A +{ +public: + //$void foo() const;$// + A(); +}; + +#endif /*A_H_*/ +//@A.cpp +#include "A.h" + +A::A() +{ +} + +//= +#include "A.h" + +void A::foo() const +{ +} + +A::A() +{ +} + +//!method with int return value +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +#ifndef A_H_ +#define A_H_ + +class A +{ +public: + A(); + //$int foo();$// +}; + +#endif /*A_H_*/ +//@A.cpp +#include "A.h" + +A::A() +{ +} + +//= +#include "A.h" + +A::A() +{ +} + +int A::foo() +{ + +} + +//!method with two int parameters +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +#ifndef A_H_ +#define A_H_ + +class A +{ +public: + A(); + //$int foo(int param1, int param2);$// +}; + +#endif /*A_H_*/ +//@A.cpp +#include "A.h" + +A::A() +{ +} + +//= +#include "A.h" + +A::A() +{ +} + +int A::foo(int param1, int param2) +{ + +} + +//!method defined in header +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +class A +{ +public: + A(); + //$void test();$// +}; + +A::A() +{ +} + +//= +class A +{ +public: + A(); + void test(); +}; + +A::A() +{ +} + +inline void A::test() +{ + +} + +//!implement a function at end of source file +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +void function_with_impl(); +//$void function();$// +//@A.cpp +void function_with_impl() +{ +} +//= +void function_with_impl() +{ +} + +void function() +{ + +} +//!implement with namespace +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +namespace NameSpace +{ + class ClassInNamespace + { + public: + int other_test(); + //$void test();$// + }; +} + +//@A.cpp +#include "A.h" +void NameSpace::ClassInNamespace::other_test() +{ +} +//= +#include "A.h" +void NameSpace::ClassInNamespace::other_test() +{ +} + +void NameSpace::ClassInNamespace::test() +{ + +} +//!implement function within namespace +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +namespace OuterSpace { + namespace NameSpace + { + int test(); + //$int test2();$// + } +} + +//@A.cpp +#include "A.h" +namespace OuterSpace { + int NameSpace::test() + { + } +} +//= +#include "A.h" +namespace OuterSpace { + int NameSpace::test() + { + } + + int NameSpace::test2() + { + + } +} +//!implement function within namespaces +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +namespace OuterSpace { + namespace NameSpace + { + int test(); + //$int test2();$// + } +} + +//@A.cpp +#include "A.h" +namespace OuterSpace { + namespace NameSpace { + int test() + { + } + } +} +//= +#include "A.h" +namespace OuterSpace { + namespace NameSpace { + int test() + { + } + + int test2() + { + + } + } +} +//!class template member functions +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +template class A +{ +public: + A(); + //$void test();$// +}; + +template A::A() +{ +} + +//= +template class A +{ +public: + A(); + void test(); +}; + +template A::A() +{ +} + +template inline void A::test() +{ + +} + +//!class template member functions with multiple templates +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h +template class A +{ +public: + A(); + //$void test();$// +}; + +template A::A() +{ +} + +//= +template class A +{ +public: + A(); + void test(); +}; + +template A::A() +{ +} + +template inline void A::test() +{ + +} + +//!with default parameters +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h + +class Class +{ +public: + //$void test(int param1, int param2 = 5, int param3 = 10);$// +}; + + +//@A.cpp +#include "A.h" + +//= +#include "A.h" + +void Class::test(int param1, int param2, int param3) +{ + +} + +//!static method +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h + +class Class +{ +public: + //$static void test();$// +}; + + +//@A.cpp +#include "A.h" + +//= +#include "A.h" + +void Class::test() +{ + +} + +//!member class +//#org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest +//@.config +filename=A.h +//@A.h + +class Demo +{ + class SubClass + { + //$void test();$// + }; +}; + + +//@A.cpp +#include "A.h" + +//= +#include "A.h" + +void Demo::SubClass::test() +{ + +} + diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java index e7cb4b67ece..48c6c63b6b7 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java @@ -31,7 +31,7 @@ public class RefactoringTestSuite extends TestSuite { suite.addTest(RefactoringTester.suite("ExtractConstantRefactoringTests", "resources/refactoring/ExtractConstant.rts")); suite.addTest(RefactoringTester.suite("HideMethodRefactoringTests", "resources/refactoring/HideMethod.rts")); suite.addTest(UtilTestSuite.suite()); + suite.addTest(RefactoringTester.suite("ImplementMethodRefactoringTests", "resources/refactoring/ImplementMethod.rts")); return suite; } - } diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/implementmethod/ImplementMethodRefactoringTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/implementmethod/ImplementMethodRefactoringTest.java new file mode 100644 index 00000000000..a3c7efc9995 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/implementmethod/ImplementMethodRefactoringTest.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.refactoring.implementmethod; + +import java.util.Properties; +import java.util.Vector; + +import org.eclipse.core.resources.IFile; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; + +import org.eclipse.cdt.ui.tests.refactoring.RefactoringTest; +import org.eclipse.cdt.ui.tests.refactoring.TestSourceFile; + +import org.eclipse.cdt.internal.ui.refactoring.CRefactoring; +import org.eclipse.cdt.internal.ui.refactoring.implementmethod.ImplementMethodRefactoring; + +/** + * @author Mirko Stocker + * + */ +public class ImplementMethodRefactoringTest extends RefactoringTest { + + protected int warnings; + + public ImplementMethodRefactoringTest(String name,Vector files) { + super(name, files); + } + + @Override + protected void runTest() throws Throwable { + + IFile refFile = project.getFile(fileName); + + CRefactoring refactoring = new ImplementMethodRefactoring(refFile, selection, null); + refactoring.lockIndex(); + try { + RefactoringStatus checkInitialConditions = refactoring.checkInitialConditions(NULL_PROGRESS_MONITOR); + + assertConditionsOk(checkInitialConditions); + + refactoring.checkFinalConditions(NULL_PROGRESS_MONITOR); + RefactoringStatus finalConditions = refactoring.checkFinalConditions(NULL_PROGRESS_MONITOR); + if (warnings == 0) { + Change createChange = refactoring.createChange(NULL_PROGRESS_MONITOR); + assertConditionsOk(finalConditions); + createChange.perform(NULL_PROGRESS_MONITOR); + } else { + assertConditionsWarning(finalConditions, warnings); + } + compareFiles(fileMap); + } + finally { + refactoring.unlockIndex(); + } + } + + @Override + protected void configureRefactoring(Properties refactoringProperties) { + warnings = new Integer(refactoringProperties.getProperty("warnings", "0")).intValue(); //$NON-NLS-1$//$NON-NLS-2$ + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/PseudoNameGeneratorTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/PseudoNameGeneratorTest.java new file mode 100644 index 00000000000..46346522c4f --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/PseudoNameGeneratorTest.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.refactoring.utils; + +import junit.framework.TestCase; + +import org.eclipse.cdt.internal.ui.refactoring.utils.PseudoNameGenerator; + +/** + * @author Mirko Stocker + * + */ +public class PseudoNameGeneratorTest extends TestCase { + + private static final String CHAR2 = "char2"; //$NON-NLS-1$ + private static final String INT3 = "int3"; //$NON-NLS-1$ + private static final String INT2 = "int2"; //$NON-NLS-1$ + private static final String CHAR = "char"; //$NON-NLS-1$ + private static final String CHAR1 = "char1"; //$NON-NLS-1$ + private static final String INT = "int"; //$NON-NLS-1$ + private static final String INT1 = "int1"; //$NON-NLS-1$ + private PseudoNameGenerator pseudoNameGenerator; + + @Override + protected void setUp() throws Exception { + pseudoNameGenerator = new PseudoNameGenerator(); + } + + public void testNonConflictingCase() { + assertEquals(INT1, pseudoNameGenerator.generateNewName(INT)); + } + + public void testMultipleNonConflictingCase() { + assertEquals(INT1, pseudoNameGenerator.generateNewName(INT)); + assertEquals(CHAR1, pseudoNameGenerator.generateNewName(CHAR)); + } + + public void testConflictingCase() { + pseudoNameGenerator.addExistingName(INT1); + assertEquals(INT2, pseudoNameGenerator.generateNewName(INT)); + } + + public void testMultipleConflictsCase() { + pseudoNameGenerator.addExistingName(INT1); + pseudoNameGenerator.addExistingName(CHAR1); + assertEquals(INT2, pseudoNameGenerator.generateNewName(INT)); + assertEquals(INT3, pseudoNameGenerator.generateNewName(INT)); + assertEquals(CHAR2, pseudoNameGenerator.generateNewName(CHAR)); + } + + public void testWithNamespace() { + assertEquals("string1", pseudoNameGenerator.generateNewName("std::string")); //$NON-NLS-1$//$NON-NLS-2$ + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/UtilTestSuite.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/UtilTestSuite.java index 37bf539f4fc..bdeca35b3f5 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/UtilTestSuite.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/UtilTestSuite.java @@ -26,6 +26,7 @@ public class UtilTestSuite extends TestSuite { UtilTestSuite suite = new UtilTestSuite(); suite.addTest(IdentifierHelperTest.suite()); suite.addTest(RefactoringTester.suite("TranslationUnitHelperTest", "resources/refactoring/TranslationunitHelper.rts")); //$NON-NLS-1$ //$NON-NLS-2$ + suite.addTestSuite(PseudoNameGeneratorTest.class); return suite; } } diff --git a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF index 5abd0be4a26..1a2706068cc 100644 --- a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF @@ -33,6 +33,7 @@ Export-Package: org.eclipse.cdt.internal.corext;x-internal:=true, org.eclipse.cdt.internal.ui.refactoring.extractconstant;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.extractfunction;x-friends:="org.eclipse.cdt.ui.tests", 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.rename;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.utils;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.search;x-internal:=true, diff --git a/core/org.eclipse.cdt.ui/plugin.properties b/core/org.eclipse.cdt.ui/plugin.properties index 5bd80b700b7..351dd8acb5c 100644 --- a/core/org.eclipse.cdt.ui/plugin.properties +++ b/core/org.eclipse.cdt.ui/plugin.properties @@ -146,6 +146,8 @@ ActionDefinition.extractConstant.name= Extract Constant - Refactoring ActionDefinition.extractConstant.description= Extract a constant for the selected expression ActionDefinition.extractFunction.name= Extract Function - Refactoring ActionDefinition.extractFunction.description= Extract a function for the selected list of expressions or statements +ActionDefinition.implementMethod.name= Implement Method - Source Generation +ActionDefinition.implementMethod.description= Implements a method for a selected method declaration # Action Set CodingActionSet.label= C/C++ Coding @@ -156,6 +158,7 @@ Refactoring.renameAction.label=Re&name... Refactoring.extractConstant.label=Extr&act Constant... Refactoring.extractFunction.label=Extract &Function... (work in progress) Refactoring.hideMethod.label=Hide Member Function... (work in progress) +Refactoring.implementMethod.label=Impl&ement Method... (work in progress) CEditor.name=C/C++ Editor diff --git a/core/org.eclipse.cdt.ui/plugin.xml b/core/org.eclipse.cdt.ui/plugin.xml index 9e7a19655c9..3fbe2853638 100644 --- a/core/org.eclipse.cdt.ui/plugin.xml +++ b/core/org.eclipse.cdt.ui/plugin.xml @@ -1171,6 +1171,13 @@ id="org.eclipse.cdt.ui.actions.ExtractConstant" retarget="true"> + + + + implement method action + * (value "org.eclipse.cdt.ui.refactor.implement.method"). + */ + public static final String IMPLEMENT_METHOD= "org.eclipse.cdt.ui.refactor.implement.method"; //$NON-NLS-1$ + /** * Action definition ID of the refactor -> undo action * (value "org.eclipse.cdt.ui.edit.text.undo.action"). diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/dialogs/Messages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/dialogs/Messages.java index be8666fcec7..0915a7d1202 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/dialogs/Messages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/dialogs/Messages.java @@ -27,6 +27,9 @@ public final class Messages extends NLS { public static String ExtractInputPage_EnterName; public static String ExtractInputPage_CheckName; public static String VisibilitySelectionPanel_AccessModifier; + public static String ValidatingLabeledTextField_CantBeEmpty; + public static String ValidatingLabeledTextField_InvalidCharacters; + public static String ValidatingLabeledTextField_DuplicatedNames; static { NLS.initializeMessages(BUNDLE_NAME, Messages.class); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/dialogs/ValidatingLabeledTextField.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/dialogs/ValidatingLabeledTextField.java new file mode 100644 index 00000000000..364bae35910 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/dialogs/ValidatingLabeledTextField.java @@ -0,0 +1,198 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ + + +package org.eclipse.cdt.internal.ui.refactoring.dialogs; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Text; + + +/** + * @author Mirko Stocker + * + * Text field with a description and error handling using the Validator-Callback. Can also be used for multiple inputs. + * + */ +public class ValidatingLabeledTextField extends Composite { + + private static final String EMPTY_STRING = ""; //$NON-NLS-1$ + + private final Map validationStatus = new HashMap(); + + private final ArrayList inputTextListeners = new ArrayList(); + + private final Color errorColor = new Color(getShell().getDisplay(), new RGB(255, 208, 196)); + + /** + * The Validator is used for feedback about the validation status of the inputs and to validate the input. + */ + public static abstract class Validator { + + /** + * Is called if all input texts contain valid input. + */ + public void hasErrors() {} + + /** + * Is called if any input text contains invalid input. + */ + public void hasNoErrors() {} + + /** + * @param text the new value of the field + * @return whether the value is valid or not + */ + public abstract boolean isValidInput(String text); + + public String errorMessageForEmptyField() { + return Messages.ValidatingLabeledTextField_CantBeEmpty; + } + + public String errorMessageForInvalidInput() { + return Messages.ValidatingLabeledTextField_InvalidCharacters; + } + + public String errorMessageForDuplicateValues() { + return Messages.ValidatingLabeledTextField_DuplicatedNames; + } + } + + public ValidatingLabeledTextField(Composite parent, int style) { + super(parent, style); + + GridLayout gridLayout = new GridLayout(); + gridLayout.numColumns = 4; + + setLayout(gridLayout); + } + + public ValidatingLabeledTextField(Composite parent) { + this(parent, SWT.NONE); + } + + public void addElement(String description, String initialText, boolean readOnly, final Validator validator) { + + Label label = new Label(this, SWT.NONE); + label.setText(description); + label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING)); + + final Text textField = new Text(this, SWT.BORDER |SWT.SINGLE); + textField.setText(initialText); + textField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + if(readOnly) { + //readOnly inputs are always valid: + validationStatus.put(textField, Boolean.TRUE); + textField.setEnabled(false); + return; + } + validationStatus.put(textField, Boolean.FALSE); + + final Label errorImageLabel = new Label(this, SWT.NONE); + errorImageLabel.setImage(JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_ERROR)); + errorImageLabel.setLayoutData(new GridData()); + errorImageLabel.setVisible(false); + + final Label errorLabel = new Label(this, SWT.NONE); + errorLabel.setLayoutData(new GridData()); + + final Color defaultColor = textField.getBackground(); + + Listener listener = new Listener(){ + + @SuppressWarnings("unchecked") + public void checkField() { + String newName = textField.getText(); + + + boolean isEmpty = (newName.length() == 0); + + boolean isNameAlreadyInUse = nameAlreadyInUse(textField, newName); + boolean isValid = validator.isValidInput(newName); + + if (isValid && !isNameAlreadyInUse && !isEmpty) { + setErrorStatus(EMPTY_STRING); + } else if (isEmpty) { + setErrorStatus(validator.errorMessageForEmptyField()); + } else if (!isValid) { + setErrorStatus(validator.errorMessageForInvalidInput()); + } else if (isNameAlreadyInUse) { + setErrorStatus(validator.errorMessageForDuplicateValues()); + } + + validationStatus.put(textField, Boolean.valueOf(isValid && !isNameAlreadyInUse && !isEmpty)); + + if(validationStatus.values().contains(Boolean.FALSE) || isEmpty || isNameAlreadyInUse) { + validator.hasErrors(); + } else { + validator.hasNoErrors(); + } + + layout(); + + // recheck all other listeners in case duplicate names have been resolved, + // but remove this first to avoid an infinite loop + inputTextListeners.remove(this); + for(Listener listener : (ArrayList) inputTextListeners.clone()) { + listener.handleEvent(null); + } + inputTextListeners.add(this); + } + + private boolean nameAlreadyInUse(final Text textField, String newName) { + for (Text text : validationStatus.keySet()) { + if(text != textField && text.getText().equals(newName)) { + return true; + } + } + return false; + } + + private void setErrorStatus(String errorMessage) { + if (EMPTY_STRING.equals(errorMessage)) { + textField.setBackground(defaultColor); + errorLabel.setText(EMPTY_STRING); + errorImageLabel.setVisible(false); + } else { + textField.setBackground(errorColor); + errorLabel.setText(errorMessage); + errorImageLabel.setVisible(true); + } + } + + public void handleEvent(Event event) { + checkField(); + }}; + + //we need to keep a list of all listeners so we get access from other textfields to resolve duplicate names + inputTextListeners.add(listener); + + listener.handleEvent(null); + + textField.addListener(SWT.Modify, listener); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/dialogs/messages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/dialogs/messages.properties index 0c23f89c629..8a340fe4d45 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/dialogs/messages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/dialogs/messages.properties @@ -15,3 +15,6 @@ ExtractInputPage_ReplaceInSubclass=Replace in subclass: ExtractInputPage_EnterName=Enter a name ExtractInputPage_CheckName=Check Name: {0} VisibilitySelectionPanel_AccessModifier=&Access modifier: +ValidatingLabeledTextField_CantBeEmpty=Cannot be empty +ValidatingLabeledTextField_InvalidCharacters=Invalid characters +ValidatingLabeledTextField_DuplicatedNames=Duplicated name diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoring.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoring.java new file mode 100644 index 00000000000..3cf383b9be4 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoring.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.implementmethod; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; + +import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamedTypeSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateParameter; +import org.eclipse.cdt.core.dom.rewrite.ASTRewrite; +import org.eclipse.cdt.core.model.ICElement; + +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTCompoundStatement; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFunctionDeclarator; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFunctionDefinition; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTName; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTemplateDeclaration; + +import org.eclipse.cdt.internal.ui.refactoring.CRefactoring; +import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector; +import org.eclipse.cdt.internal.ui.refactoring.utils.DefinitionFinder; +import org.eclipse.cdt.internal.ui.refactoring.utils.NameHelper; +import org.eclipse.cdt.internal.ui.refactoring.utils.NodeHelper; +import org.eclipse.cdt.internal.ui.refactoring.utils.PseudoNameGenerator; +import org.eclipse.cdt.internal.ui.refactoring.utils.SelectionHelper; + +/** + * Main class of the ImplementMethodRefactoring (Source generator). + * Checks conditions, finds insert location and generates the ImplementationNode. + * + * @author Mirko Stocker, Lukas Felber + * + */ +public class ImplementMethodRefactoring extends CRefactoring { + + private IASTSimpleDeclaration methodDeclaration; + private InsertLocation insertLocation; + private final Set generatedNames = new HashSet(); + + public ImplementMethodRefactoring(IFile file, ISelection selection, ICElement element) { + super(file, selection, element); + } + + public boolean needsAdditionalArgumentNames() { + for (IASTParameterDeclaration parameterDeclaration : getParameters()) { + if(parameterDeclaration.getDeclarator().getName().toCharArray().length < 1) { + return true; + } + } + return false; + } + + public IASTParameterDeclaration[] getParameters() { + if(methodDeclaration.getDeclarators().length < 1) { + return null; + } + return ((ICPPASTFunctionDeclarator) methodDeclaration.getDeclarators()[0]).getParameters(); + } + + @Override + public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { + SubMonitor sm = SubMonitor.convert(pm, 8); + super.checkInitialConditions(sm.newChild(6)); + + methodDeclaration = SelectionHelper.findFirstSelectedDeclaration(region, unit); + + if (methodDeclaration == null) { + initStatus.addFatalError("No method selected"); //$NON-NLS-1$ + return initStatus; + } + + if(isProgressMonitorCanceld(sm, initStatus))return initStatus; + sm.worked(1); + + if (DefinitionFinder.getDefinition(methodDeclaration, file) != null) { + initStatus.addFatalError("This method already has an implementation."); //$NON-NLS-1$ + return initStatus; + } + if(isProgressMonitorCanceld(sm, initStatus))return initStatus; + sm.worked(1); + sm.done(); + return initStatus; + } + + + @Override + protected void collectModifications(IProgressMonitor pm, ModificationCollector collector) throws CoreException, OperationCanceledException { + findInsertLocation(); + IASTTranslationUnit targetUnit = insertLocation.getTargetTranslationUnit(); + IASTNode parent = insertLocation.getPartenOfNodeToInsertBefore(); + IASTNode insertNode = createFunctionDefinition(targetUnit); + IASTNode nodeToInsertBefore = insertLocation.getNodeToInsertBefore(); + ASTRewrite rewrite = collector.rewriterForTranslationUnit(targetUnit); + rewrite.insertBefore(parent, nodeToInsertBefore, insertNode, null); + } + + private void findInsertLocation() throws CoreException { + insertLocation = MethodDefinitionInsertLocationFinder.find( + methodDeclaration.getFileLocation(), methodDeclaration + .getParent(), file); + + if (!insertLocation.hasFile()) { + insertLocation.setInsertFile(file); + insertLocation.setNodeToInsertAfter(NodeHelper + .getTopLevelParent(methodDeclaration)); + } + } + + private IASTNode createFunctionDefinition(IASTTranslationUnit unit) { + return createFunctionDefinition( + methodDeclaration.getDeclSpecifier(), + (ICPPASTFunctionDeclarator) methodDeclaration.getDeclarators()[0], + methodDeclaration.getParent(), unit); + } + + public IASTNode createFunctionDefinition() { + return createFunctionDefinition(unit); + } + + private IASTNode createFunctionDefinition(IASTDeclSpecifier declSpecifier, ICPPASTFunctionDeclarator functionDeclarator, IASTNode declarationParent, IASTTranslationUnit unit) { + + IASTFunctionDefinition func = new CPPASTFunctionDefinition(); + func.setParent(unit); + + if(declSpecifier instanceof ICPPASTDeclSpecifier) { + ((ICPPASTDeclSpecifier) declSpecifier).setVirtual(false); + } + + if(Path.fromOSString(methodDeclaration.getNodeLocations()[0].asFileLocation().getFileName()).equals(insertLocation.getInsertFile().getLocation())) { + declSpecifier.setInline(true); + } + + if(declSpecifier.getStorageClass() == IASTDeclSpecifier.sc_static) { + declSpecifier.setStorageClass(IASTDeclSpecifier.sc_unspecified); + } + + func.setDeclSpecifier(declSpecifier); + + ICPPASTQualifiedName qname = createQualifiedNameFor(functionDeclarator, declarationParent); + + CPPASTFunctionDeclarator newFunctionDeclarator = new CPPASTFunctionDeclarator(); + newFunctionDeclarator.setName(qname); + newFunctionDeclarator.setConst(functionDeclarator.isConst()); + + PseudoNameGenerator pseudoNameGenerator = new PseudoNameGenerator(); + + for(IASTParameterDeclaration parameter : getParameters()) { + if(parameter.getDeclarator().getName().toString().length() > 0) { + pseudoNameGenerator.addExistingName(parameter.getDeclarator().getName().toString()); + } + } + + for(IASTParameterDeclaration parameter : getParameters()) { + if(parameter.getDeclarator().getName().toString().length() < 1) { + + IASTDeclSpecifier parameterDeclSpecifier = parameter.getDeclSpecifier(); + String typeName; + if(parameterDeclSpecifier instanceof ICPPASTNamedTypeSpecifier) { + typeName = ((ICPPASTNamedTypeSpecifier) parameterDeclSpecifier).getName().getRawSignature(); + } else { + typeName = parameterDeclSpecifier.getRawSignature(); + } + + String generateNewName = pseudoNameGenerator.generateNewName(typeName); + generatedNames.add(generateNewName); + parameter.getDeclarator().setName(new CPPASTName(generateNewName.toCharArray())); + } + newFunctionDeclarator.addParameterDeclaration(parameter); + } + + removeAllDefaultParameters(newFunctionDeclarator); + + func.setDeclarator(newFunctionDeclarator); + func.setBody(new CPPASTCompoundStatement()); + + if(classHasTemplates(declarationParent)) { + CPPASTTemplateDeclaration templateDeclaration = new CPPASTTemplateDeclaration(); + templateDeclaration.setParent(unit); + + for(ICPPASTTemplateParameter templateParameter : ((ICPPASTTemplateDeclaration) declarationParent.getParent().getParent() ).getTemplateParameters()) { + templateDeclaration.addTemplateParamter(templateParameter); + } + + templateDeclaration.setDeclaration(func); + return templateDeclaration; + } + + return func; + } + + private void removeAllDefaultParameters(ICPPASTFunctionDeclarator functionDeclarator) { + for (IASTParameterDeclaration parameterDeclaration : functionDeclarator.getParameters()) { + parameterDeclaration.getDeclarator().setInitializer(null); + } + } + + private ICPPASTQualifiedName createQualifiedNameFor(IASTFunctionDeclarator functionDeclarator, IASTNode declarationParent) { + + int insertOffset; + if(insertLocation.getNodeToInsertBefore() == null) { + insertOffset = 0; + } else { + insertOffset = insertLocation.getPartenOfNodeToInsertBefore().getFileLocation().getNodeOffset(); + } + return NameHelper.createQualifiedNameFor(functionDeclarator.getName(), file, region.getOffset(), insertLocation.getInsertFile(), insertOffset); + } + + private boolean classHasTemplates(IASTNode declarationParent) { + return declarationParent.getParent() != null && declarationParent.getParent().getParent() instanceof ICPPASTTemplateDeclaration; + } + + public IASTSimpleDeclaration getMethodDeclaration() { + return methodDeclaration; + } + + public Set getGeneratedNames() { + return generatedNames; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoringRunner.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoringRunner.java new file mode 100644 index 00000000000..a63ec8b646c --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoringRunner.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.implementmethod; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.window.IShellProvider; +import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; + +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.ui.CUIPlugin; + +import org.eclipse.cdt.internal.ui.refactoring.RefactoringRunner; + +/** + * @author Lukas Felber + * + */ +public class ImplementMethodRefactoringRunner extends RefactoringRunner { + + public ImplementMethodRefactoringRunner(IFile file, ISelection selection, ICElement element, IShellProvider shellProvider) { + super(file, selection, element, shellProvider); + } + + @Override + public void run() { + ImplementMethodRefactoring refactoring = new ImplementMethodRefactoring(file, selection, celement); + ImplementMethodRefactoringWizard wizard = new ImplementMethodRefactoringWizard(refactoring); + RefactoringWizardOpenOperation operator = new RefactoringWizardOpenOperation(wizard); + + try { + refactoring.lockIndex(); + try { + operator.run(shellProvider.getShell(), refactoring.getName()); + } + finally { + refactoring.unlockIndex(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (CoreException e) { + CUIPlugin.log(e); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoringWizard.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoringWizard.java new file mode 100644 index 00000000000..453bcf3e0fe --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ImplementMethodRefactoringWizard.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.implementmethod; + +import org.eclipse.ltk.ui.refactoring.RefactoringWizard; + +/** + * @author Mirko Stocker + * + */ +public class ImplementMethodRefactoringWizard extends RefactoringWizard { + + private final ImplementMethodRefactoring refactoring; + + public ImplementMethodRefactoringWizard(ImplementMethodRefactoring refactoring) { + super(refactoring, WIZARD_BASED_USER_INTERFACE); + this.refactoring = refactoring; + } + + @Override + protected void addUserInputPages() { + if(refactoring.needsAdditionalArgumentNames()) { + addPage(new ParameterNamesInputPage(refactoring)); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/InsertLocation.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/InsertLocation.java new file mode 100644 index 00000000000..1301ce8fc57 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/InsertLocation.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.refactoring.implementmethod; + +import org.eclipse.core.resources.IFile; + +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; + +import org.eclipse.cdt.internal.ui.refactoring.utils.TranslationUnitHelper; + +/** + * Is returned when using the find-method of the MethodDefinitionInsertLocationFinder. + * Contains all the infos needet to insert at the correct position. + * + * @author Lukas Felber + * + */ +public class InsertLocation { + private IFile insertFile; + private IASTNode nodeToInsertAfter; + private IASTNode nodeToInsertBefore; + private IASTTranslationUnit targetTranslationUnit; + + public boolean hasAnyNode() { + return nodeToInsertAfter == null && nodeToInsertBefore == null; + } + + public IASTNode getNodeToInsertBefore() { + return nodeToInsertBefore; + } + + public IASTNode getPartenOfNodeToInsertBefore() { + IASTNode affectedNode = getAffectedNode(); + return (affectedNode != null) ? affectedNode.getParent() : getTargetTranslationUnit(); + } + + private IASTNode getAffectedNode() { + IASTNode concernedNode = (nodeToInsertBefore != null) ? nodeToInsertBefore : nodeToInsertAfter; + return concernedNode; + } + + public IFile getInsertFile() { + return insertFile; + } + + public void setInsertFile(IFile insertFile) { + this.insertFile = insertFile; + } + + public void setNodeToInsertAfter(IASTNode nodeToInsertAfter) { + this.nodeToInsertAfter = nodeToInsertAfter; + } + + public void setNodeToInsertBefore(IASTNode nodeToInsertBefore) { + this.nodeToInsertBefore = nodeToInsertBefore; + } + + public boolean hasFile() { + return insertFile != null; + } + + public IASTTranslationUnit getTargetTranslationUnit() { + if(targetTranslationUnit == null) { + loadTargetTranslationUnit(); + } + return targetTranslationUnit; + + } + + private void loadTargetTranslationUnit() { + IASTNode affectedNode = getAffectedNode(); + if(affectedNode != null) { + targetTranslationUnit = affectedNode.getTranslationUnit(); + } else if(hasFile()) { + targetTranslationUnit = TranslationUnitHelper.loadTranslationUnit(insertFile); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/Messages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/Messages.java new file mode 100644 index 00000000000..21a72080a39 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/Messages.java @@ -0,0 +1,32 @@ +/******************************************************************************* +* Copyright (c) 2005 IBM Corporation and others. +* +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* IBM - initial API and implementation +*******************************************************************************/ + +package org.eclipse.cdt.internal.ui.refactoring.implementmethod; + +import org.eclipse.osgi.util.NLS; + +public final class Messages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.cdt.internal.ui.refactoring.implementmethod.messages";//$NON-NLS-1$ + + private Messages() { + // Do not instantiate + } + + public static String ImplementMethodRefactoring_MethodDefinition; + public static String ParameterNamesInputPage_Title; + public static String ParameterNamesInputPage_CompleteMissingMails; + + static { + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/MethodDefinitionInsertLocationFinder.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/MethodDefinitionInsertLocationFinder.java new file mode 100644 index 00000000000..3cbb350b383 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/MethodDefinitionInsertLocationFinder.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.refactoring.implementmethod; + +import java.util.Collection; +import java.util.Vector; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; + +import org.eclipse.cdt.core.dom.ast.IASTDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTFileLocation; +import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; + +import org.eclipse.cdt.internal.ui.refactoring.utils.DefinitionFinder; +import org.eclipse.cdt.internal.ui.refactoring.utils.FileHelper; +import org.eclipse.cdt.internal.ui.refactoring.utils.NodeHelper; + +/** + * Findes the information that are needed to tell where a MethodDefinition of a certain method declaration should be inserted. + * + * @author Mirko Stocker, Lukas Felber + * + */ +public class MethodDefinitionInsertLocationFinder { + + private static IASTNode findFunctionDefinitionInParents(IASTNode node) { + if(node == null) { + return null; + } else if(node instanceof IASTFunctionDefinition) { + return node; + } + return findFunctionDefinitionInParents(node.getParent()); + } + + private static IASTNode findFirstSurroundingParentFunctionNode(IASTNode definition) { + IASTNode functionDefinitionInParents = findFunctionDefinitionInParents(definition); + if(functionDefinitionInParents == null || functionDefinitionInParents.getNodeLocations().length == 0) { + return null; + } + return functionDefinitionInParents; + } + + public static InsertLocation find(IASTFileLocation methodDeclarationLocation, IASTNode parent, IFile file) throws CoreException { + IASTName definition = null; + IASTDeclaration[] declarations = NodeHelper.getDeclarations(parent); + InsertLocation result = new InsertLocation(); + + for (IASTSimpleDeclaration simpleDeclaration : getAllPreviousIASTSimpleDeclarationsFromClassInReverseOrder(declarations, methodDeclarationLocation)) { + definition = DefinitionFinder.getDefinition(simpleDeclaration, file); + + if (definition != null) { + result.setNodeToInsertAfter(findFirstSurroundingParentFunctionNode(definition)); + + result.setInsertFile(FileHelper.getIFilefromIASTNode(definition)); + } + } + + for (IASTSimpleDeclaration simpleDeclaration : getAllFollowingIASTSimpleDeclarationsFromClass(declarations, methodDeclarationLocation)) { + definition = DefinitionFinder.getDefinition(simpleDeclaration, file); + + if (definition != null) { + result.setNodeToInsertBefore(findFirstSurroundingParentFunctionNode(definition)); + + result.setInsertFile(FileHelper.getIFilefromIASTNode(definition)); + } + } + + if(!result.hasAnyNode()) { + return result; + } + + IPath path = file.getLocation().removeFileExtension().addFileExtension("cpp"); //$NON-NLS-1$ + IFile fileForLocation = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(path); + + if(fileForLocation != null && fileForLocation.exists()) { + result.setInsertFile(fileForLocation); + } + return result; + } + + + /** + * Search the given class for all IASTSimpleDeclarations occuring before 'method' and return them in reverse order. + * + * @param declarations to be searched + * @param methodPosition on which the search aborts + * @return all declarations, sorted in reverse order + */ + private static Collection getAllPreviousIASTSimpleDeclarationsFromClassInReverseOrder(IASTDeclaration[] declarations, IASTFileLocation methodPosition) { + Vector allIASTSimpleDeclarations = new Vector(); + for (IASTDeclaration decl : declarations) { + if (decl.getFileLocation().getStartingLineNumber() >= methodPosition.getStartingLineNumber()) { + return allIASTSimpleDeclarations; + } + if (isMemberFunctionDeclaration(decl)) { + allIASTSimpleDeclarations.insertElementAt((IASTSimpleDeclaration) decl, 0); + } + } + return allIASTSimpleDeclarations; + } + + private static Collection getAllFollowingIASTSimpleDeclarationsFromClass(IASTDeclaration[] declarations, IASTFileLocation methodPosition) { + Vector allIASTSimpleDeclarations = new Vector(); + + for (IASTDeclaration decl : declarations) { + if (isMemberFunctionDeclaration(decl) && decl.getFileLocation().getStartingLineNumber() > methodPosition.getStartingLineNumber() ) { + allIASTSimpleDeclarations.add((IASTSimpleDeclaration) decl); + } + } + return allIASTSimpleDeclarations; + } + + private static boolean isMemberFunctionDeclaration(IASTDeclaration decl) { + return decl instanceof IASTSimpleDeclaration && ((IASTSimpleDeclaration) decl).getDeclarators().length > 0 && ((IASTSimpleDeclaration) decl).getDeclarators()[0] instanceof IASTFunctionDeclarator; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ParameterNamesInputPage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ParameterNamesInputPage.java new file mode 100644 index 00000000000..01a6361e246 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/ParameterNamesInputPage.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.refactoring.implementmethod; + +import java.util.HashMap; + +import org.eclipse.ltk.ui.refactoring.UserInputWizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; + +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTName; + +import org.eclipse.cdt.internal.ui.preferences.formatter.TranslationUnitPreview; +import org.eclipse.cdt.internal.ui.refactoring.dialogs.ValidatingLabeledTextField; +import org.eclipse.cdt.internal.ui.refactoring.utils.NameHelper; + +/** + * InputPage used by the ImplementMethod refactoring if its necessary to enteraditional parameter names. + * + * @author Mirko Stocker + * + */ +public class ParameterNamesInputPage extends UserInputWizardPage { + + private final ImplementMethodRefactoring refactoring; + + private TranslationUnitPreview translationUnitPreview; + + public ParameterNamesInputPage(ImplementMethodRefactoring implementMethodRefactoring) { + super(Messages.ParameterNamesInputPage_Title); + this.refactoring = implementMethodRefactoring; + } + + public void createControl(Composite parent) { + + Composite superComposite = new Composite(parent, SWT.NONE); + + superComposite.setLayout(new GridLayout()); + + Label label = new Label(superComposite, SWT.NONE); + label.setText(Messages.ParameterNamesInputPage_CompleteMissingMails); + label.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL)); + + ValidatingLabeledTextField validatingLabeledTextField = new ValidatingLabeledTextField(superComposite); + validatingLabeledTextField.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL)); + + for (final IASTParameterDeclaration parameterDeclaration : refactoring.getParameters()) { + + String type = parameterDeclaration.getDeclSpecifier().getRawSignature(); + String content = String.valueOf(parameterDeclaration.getDeclarator().getName().toCharArray()); + boolean enabled = parameterDeclaration.getDeclarator().getName().toCharArray().length > 0; + + validatingLabeledTextField.addElement(type, content, enabled, new ValidatingLabeledTextField.Validator(){ + + @Override + public void hasErrors() { + setPageComplete(false); + } + + @Override + public void hasNoErrors() { + setPageComplete(true); + } + + @Override + public boolean isValidInput(String newName) { + boolean isValid = NameHelper.isValidLocalVariableName(newName); + + if(isValid) { + parameterDeclaration.getDeclarator().setName(new CPPASTName(newName.toCharArray())); + translationUnitPreview.setPreviewText(refactoring.createFunctionDefinition().getRawSignature()); + } + + return isValid; + }}); + } + + translationUnitPreview = new TranslationUnitPreview(new HashMap(), superComposite); + translationUnitPreview.getControl().setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL)); + translationUnitPreview.setPreviewText(refactoring.createFunctionDefinition().getRawSignature()); + + setControl(superComposite); + + setPageComplete(false); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/messages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/messages.properties new file mode 100644 index 00000000000..ce6f08648e5 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/implementmethod/messages.properties @@ -0,0 +1,15 @@ +############################################################################### +# Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik +# Rapperswil, University of applied sciences and others +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Institute for Software - initial API and implementation +# IBM Corporation +############################################################################### +ParameterNamesInputPage_Title=Implement Method +ParameterNamesInputPage_CompleteMissingMails=Please complete the missing variable names: +ImplementMethodRefactoring_MethodDefinition=Method Definition diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/DefinitionFinder.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/DefinitionFinder.java new file mode 100644 index 00000000000..8a0127512fc --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/DefinitionFinder.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.refactoring.utils; + +import java.util.TreeMap; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.IASTDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; +import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.index.IIndexName; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.ui.CUIPlugin; + +import org.eclipse.cdt.internal.ui.refactoring.Container; + +/** + * Helper class to find definitions. + * + * @author Lukas Felber + * + */ +public class DefinitionFinder { + + public static IASTName getDefinition(IASTSimpleDeclaration simpleDeclaration, IFile file) throws CoreException { + IASTDeclarator declarator = simpleDeclaration.getDeclarators()[0]; + IBinding resolveBinding = declarator.getName().resolveBinding(); + return DefinitionFinder.getDefinition(declarator.getName(), resolveBinding, file); + } + + @SuppressWarnings("null") // Can't be null + public static IASTName getDefinition(IASTName methodName, IBinding bind, IFile file) throws CoreException { + TreeMap parsedFiles = new TreeMap(); + + ITranslationUnit tu = (ITranslationUnit) CCorePlugin.getDefault().getCoreModel().create(file); + IIndex index = CCorePlugin.getIndexManager().getIndex(tu.getCProject()); + IIndexName[] pdomref = null; + + try { + index.acquireReadLock(); + pdomref = index.findDefinitions(bind); + } catch (InterruptedException e) { + IStatus status = new Status(IStatus.WARNING, CUIPlugin.PLUGIN_ID, IStatus.OK, e.getMessage(), e); + CUIPlugin.log(status); + } finally { + index.releaseReadLock(); + } + + if (pdomref == null || (pdomref != null && pdomref.length < 1)) { + return null; + } + + IASTTranslationUnit transUnit; + if (!parsedFiles.containsKey(pdomref[0].getFileLocation().getFileName())) { + String filename = pdomref[0].getFileLocation().getFileName(); + transUnit = TranslationUnitHelper.loadTranslationUnit(filename); + } else { + transUnit = parsedFiles.get(pdomref[0].getFileLocation().getFileName()); + } + return findDefinitionInTranslationUnit(transUnit, methodName, pdomref[0]); + } + + private static IASTName findDefinitionInTranslationUnit(IASTTranslationUnit transUnit, final IASTName name2, final IIndexName indexName) { + final Container defName = new Container(); + transUnit.accept(new CPPASTVisitor() { + { + shouldVisitNames = true; + } + + @Override + public int visit(IASTName name) { + if (name.isDefinition() && name.getNodeLocations().length > 0) { + IASTNodeLocation nodeLocation = name.getNodeLocations()[0]; + if (indexName.getNodeOffset() == nodeLocation.getNodeOffset() + && indexName.getNodeLength() == nodeLocation.getNodeLength() + && new Path(indexName.getFileLocation().getFileName()).equals(new Path(nodeLocation.asFileLocation().getFileName()))) { + defName.setObject(name); + return ASTVisitor.PROCESS_ABORT; + } + } + return ASTVisitor.PROCESS_CONTINUE; + } + + }); + return defName.getObject(); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/NameHelper.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/NameHelper.java new file mode 100644 index 00000000000..b6708d16a1a --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/NameHelper.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.utils; + +import java.util.regex.Pattern; + +import org.eclipse.core.resources.IFile; + +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; + +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTQualifiedName; + +/** + * Helps with IASTNames. + * + * @author Mirko Stocker + * + */ +public class NameHelper { + + + private static final String localVariableRegexp = "[a-z_A-Z]\\w*"; //$NON-NLS-1$ + + public static boolean isValidLocalVariableName(String name) { + boolean valid = Pattern.compile(localVariableRegexp).matcher(name).matches(); + return valid; /* && Keyword.getKeyword(stringToValidate, stringToValidate.length()) == null*/ //TODO Check for keywords?; + } + + /** + * Constructs the fully qualified name from the given parameters. The file and offset parameters are used to determine + * the namespace at the declaration position and the target namespace at the target position. + * + * @param declaratorName of the method or function + * @param declarationFile + * @param selectionOffset the offset in the declarationFile, usually the position or selection of the declaration + * @param insertFile the target file in which the definition is inserted + * @param insertLocation + * @return the correct name for the target + */ + public static ICPPASTQualifiedName createQualifiedNameFor(IASTName declaratorName, IFile declarationFile, int selectionOffset, IFile insertFile, int insertLocation) { + ICPPASTQualifiedName qname = new CPPASTQualifiedName(); + + IASTName[] declarationNames = NamespaceHelper.getSurroundingNamespace(declarationFile, selectionOffset).getNames(); + IASTName[] implementationNames = NamespaceHelper.getSurroundingNamespace(insertFile, insertLocation).getNames(); + + for(int i = 0; i < declarationNames.length; i++) { + if(i >= implementationNames.length) { + qname.addName(declarationNames[i]); + } else if (!String.valueOf(declarationNames[i].toCharArray()).equals(String.valueOf(implementationNames[i].toCharArray()))) { + qname.addName(declarationNames[i]); + } + } + + qname.addName(declaratorName); + return qname; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/NamespaceHelper.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/NamespaceHelper.java new file mode 100644 index 00000000000..b06abadf166 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/NamespaceHelper.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.utils; + +import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateParameter; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTNamedTypeSpecifier; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTQualifiedName; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTSimpleTypeTemplateParameter; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTemplateId; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTypeId; +import org.eclipse.core.resources.IFile; + +/** + * Helper class to find Namespace informations. + * @author Mirko Stocker + * + */ +public class NamespaceHelper { + + /** + * Returns the qualified name of all namespaces that are defined at the specified file and offset. + * + * @param insertFile + * @param offset + * @return ICPPASTQualifiedName with the names of all namespaces + */ + public static ICPPASTQualifiedName getSurroundingNamespace(final IFile insertFile, final int offset) { + + final CPPASTQualifiedName qualifiedName = new CPPASTQualifiedName(); + + TranslationUnitHelper.loadTranslationUnit(insertFile).accept(new CPPASTAllVisitor() { + + @Override + public int visit(IASTDeclSpecifier declSpec) { + if (declSpec instanceof ICPPASTCompositeTypeSpecifier && checkFileNameAndLocation(insertFile, offset, declSpec)) { + qualifiedName.addName(createNameWithTemplates(declSpec)); + } + return super.visit(declSpec); + } + + @Override + public int visit(ICPPASTNamespaceDefinition namespace) { + if (checkFileNameAndLocation(insertFile, offset, namespace)) { + qualifiedName.addName((namespace).getName()); + } + + return super.visit(namespace); + } + }); + + return qualifiedName; + } + + private static boolean checkFileNameAndLocation(final IFile insertFile, final int offset, IASTNode namespace) { + return namespace.getFileLocation().getFileName().endsWith(insertFile.getFullPath().toOSString()) + && offset >= namespace.getNodeLocations()[0].getNodeOffset() + && offset <= namespace.getNodeLocations()[0].getNodeOffset() + namespace.getNodeLocations()[0].getNodeLength(); + } + + private static IASTName createNameWithTemplates(IASTNode declarationParent) { + IASTName parentName; + parentName = ((ICPPASTCompositeTypeSpecifier) declarationParent).getName(); + + if(classHasTemplates(declarationParent)) { + CPPASTTemplateId templateId = new CPPASTTemplateId(); + templateId.setTemplateName(parentName); + + for(ICPPASTTemplateParameter templateParameter : ((ICPPASTTemplateDeclaration) declarationParent.getParent().getParent() ).getTemplateParameters()) { + + if (templateParameter instanceof CPPASTSimpleTypeTemplateParameter) { + CPPASTSimpleTypeTemplateParameter simpleTypeTemplateParameter = (CPPASTSimpleTypeTemplateParameter) templateParameter; + + CPPASTTypeId id = new CPPASTTypeId(); + + CPPASTNamedTypeSpecifier namedTypeSpecifier = new CPPASTNamedTypeSpecifier(); + namedTypeSpecifier.setName(simpleTypeTemplateParameter.getName()); + id.setDeclSpecifier(namedTypeSpecifier); + + templateId.addTemplateArgument(id); + } + } + + parentName = templateId; + } + return parentName; + } + + private static boolean classHasTemplates(IASTNode declarationParent) { + return declarationParent.getParent() != null + && declarationParent.getParent().getParent() instanceof ICPPASTTemplateDeclaration; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/NodeHelper.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/NodeHelper.java new file mode 100644 index 00000000000..e6a254fef55 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/NodeHelper.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.utils; + +import org.eclipse.cdt.core.dom.ast.IASTDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; + +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTNamespaceDefinition; +import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTranslationUnit; + +/** + * General class for common Node operations. + * + * @author Lukas Felber + * + */ +public class NodeHelper { + + public static IASTDeclaration[] getDeclarations(IASTNode parent) { + if (parent instanceof ICPPASTCompositeTypeSpecifier) { + return ((ICPPASTCompositeTypeSpecifier) parent).getMembers(); + } else if (parent instanceof CPPASTTranslationUnit) { + return ((CPPASTTranslationUnit) parent).getDeclarations(); + } else if (parent instanceof CPPASTNamespaceDefinition) { + return ((CPPASTNamespaceDefinition) parent).getDeclarations(); + } + return new IASTDeclaration[0]; + } + + + public static IASTNode findFollowingNode(IASTNode currentNode) { + if(currentNode == null || currentNode.getParent() == null) { + return null; + } + boolean match = false; + for(IASTNode actNode : getDeclarations(currentNode.getParent())) { + if(match) { + return actNode; + } + if(actNode.equals(currentNode)) { + match = true; + } + } + return null; + } + + public static IASTNode getTopLevelParent(IASTNode currentNode) { + while(currentNode != null && currentNode.getParent() != null && currentNode.getParent().getParent() != null) { + return getTopLevelParent(currentNode.getParent()); + } + return currentNode; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/PseudoNameGenerator.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/PseudoNameGenerator.java new file mode 100644 index 00000000000..883f959face --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/PseudoNameGenerator.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.utils; + +import java.util.HashSet; +import java.util.Set; + +/** + * Helps to generate new unsused names. + * + * @author Mirko Stocker + * + */ +public class PseudoNameGenerator { + + private final Set names = new HashSet(); + + public void addExistingName(String name) { + names.add(name); + } + + public String generateNewName(String typeName) { + + String[] nameParts = typeName.split("::"); //$NON-NLS-1$ + typeName = nameParts[nameParts.length - 1]; + + String newNameCandidate = null; + int index = 0; + + do { + index++; + newNameCandidate = String.format("%s%d", typeName, Integer.valueOf(index)); //$NON-NLS-1$ + } while(names.contains(newNameCandidate)); + + names.add(newNameCandidate); + + return newNameCandidate; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/SelectionHelper.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/SelectionHelper.java new file mode 100644 index 00000000000..fea4ccc9536 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/utils/SelectionHelper.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.utils; + +import org.eclipse.jface.text.Region; + +import org.eclipse.cdt.core.dom.ast.IASTDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor; + +import org.eclipse.cdt.internal.ui.refactoring.CRefactoring; +import org.eclipse.cdt.internal.ui.refactoring.Container; + +/** + * Helper class to suport operations conserning a selection. + * + * @author Mirko Stocker + * + */ +public class SelectionHelper { + + public static IASTSimpleDeclaration findFirstSelectedDeclaration(final Region textSelection, IASTTranslationUnit translationUnit) { + + final Container container = new Container(); + + translationUnit.accept(new CPPASTVisitor() { + { + shouldVisitDeclarations = true; + } + @Override + public int visit(IASTDeclaration declaration) { + if (declaration instanceof IASTSimpleDeclaration && CRefactoring.isSelectionOnExpression(textSelection, declaration)) { + container.setObject((IASTSimpleDeclaration) declaration); + } + return super.visit(declaration); + } + }); + + return container.getObject(); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/CdtActionConstants.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/CdtActionConstants.java index 871ce7c85ff..8fdad457994 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/CdtActionConstants.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/actions/CdtActionConstants.java @@ -254,6 +254,12 @@ public class CdtActionConstants { */ public static final String HIDE_METHOD= "org.eclipse.cdt.ui.actions.HideMethod"; //$NON-NLS-1$ + /** + * Refactor menu: name of standard Extract Constant global action + * (value "org.eclipse.cdt.ui.actions.ImplementMethod"). + */ + public static final String IMPLEMENT_METHOD= "org.eclipse.cdt.ui.actions.ImplementMethod"; //$NON-NLS-1$ + /** * Refactor menu: name of standard Introduce Parameter global action * (value "org.eclipse.cdt.ui.actions.IntroduceParameter"). diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/CRefactoringActionGroup.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/CRefactoringActionGroup.java index 4ad44b96550..807fff139f4 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/CRefactoringActionGroup.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/CRefactoringActionGroup.java @@ -115,6 +115,7 @@ public class CRefactoringActionGroup extends ActionGroup implements ISelectionCh private RefactoringAction fExtractConstantAction; private RefactoringAction fExtractFunctionAction; private RefactoringAction fHideMethodAction; + private RefactoringAction fImplementMethodAction; private IWorkbenchSite fSite; private List fAllActions= new ArrayList(); @@ -159,6 +160,10 @@ public class CRefactoringActionGroup extends ActionGroup implements ISelectionCh fHideMethodAction = new HideMethodAction(); fHideMethodAction.setActionDefinitionId(ICEditorActionDefinitionIds.HIDE_METHOD); fAllActions.add(fHideMethodAction); + + fImplementMethodAction = new ImplementMethodAction(); + fImplementMethodAction.setActionDefinitionId(ICEditorActionDefinitionIds.IMPLEMENT_METHOD); + fAllActions.add(fImplementMethodAction); } public void setWorkbenchSite(IWorkbenchSite site) { @@ -196,6 +201,7 @@ public class CRefactoringActionGroup extends ActionGroup implements ISelectionCh setActionHandler(actionBar, CdtActionConstants.EXTRACT_CONSTANT, fExtractConstantAction); setActionHandler(actionBar, CdtActionConstants.EXTRACT_METHOD, fExtractFunctionAction); setActionHandler(actionBar, CdtActionConstants.HIDE_METHOD, fHideMethodAction); + setActionHandler(actionBar, CdtActionConstants.IMPLEMENT_METHOD, fImplementMethodAction); } private void setActionHandler(IActionBars actionBar, String id, RefactoringAction action) { @@ -226,6 +232,7 @@ public class CRefactoringActionGroup extends ActionGroup implements ISelectionCh addAction(refactorSubmenu, fExtractConstantAction); addAction(refactorSubmenu, fExtractFunctionAction); addAction(refactorSubmenu, fHideMethodAction); + addAction(refactorSubmenu, fImplementMethodAction); refactorSubmenu.add(new Separator(GROUP_REORG2)); refactorSubmenu.add(new Separator(GROUP_TYPE)); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/ImplementMethodAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/ImplementMethodAction.java new file mode 100644 index 00000000000..2374c1f0252 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/ImplementMethodAction.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik + * Rapperswil, University of applied sciences and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Institute for Software - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.ui.refactoring.actions; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.window.IShellProvider; + +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.IMethod; +import org.eclipse.cdt.core.model.IMethodDeclaration; +import org.eclipse.cdt.core.model.ISourceReference; +import org.eclipse.cdt.core.model.IWorkingCopy; + +import org.eclipse.cdt.internal.ui.refactoring.implementmethod.ImplementMethodRefactoringRunner; + +/** + * Launches the implement method source generator (refactoring). + * + * @author Lukas Felber + */ +public class ImplementMethodAction extends RefactoringAction { + + public ImplementMethodAction() { + super(Messages.ImplementMethodAction_label); + } + + @Override + public void run(IShellProvider shellProvider, ICElement elem) { + new ImplementMethodRefactoringRunner(null, null, elem, shellProvider).run(); + } + + @Override + public void run(IShellProvider shellProvider, IWorkingCopy wc, ITextSelection selection) { + IResource res = wc.getResource(); + if (res instanceof IFile) { + new ImplementMethodRefactoringRunner((IFile) res, selection, null, shellProvider).run(); + } + } + + @Override + public void updateSelection(ICElement elem) { + super.updateSelection(elem); + if (elem instanceof IMethod || elem instanceof IMethodDeclaration == false + || elem instanceof ISourceReference == false + || ((ISourceReference) elem).getTranslationUnit().getResource() instanceof IFile == false) { + setEnabled(false); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/Messages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/Messages.java index b6b21caa8a9..c90df502eaf 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/Messages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/Messages.java @@ -19,6 +19,7 @@ public class Messages extends NLS { public static String ExtractConstantAction_label; public static String ExtractFunctionAction_label; public static String HideMethodAction_label; + public static String ImplementMethodAction_label; static { // initialize resource bundle diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/messages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/messages.properties index a21aa9ad87c..a122e389716 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/messages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/refactoring/actions/messages.properties @@ -11,5 +11,6 @@ CRefactoringActionGroup_menu=Refactor CRenameAction_label=Rename... ExtractConstantAction_label=Extract Constant... +ImplementMethodAction_label=Implement Method... (work in progress) HideMethodAction_label=Hide Member Function... (work in progress) ExtractFunctionAction_label=Extract Function... (work in progress)