diff --git a/hu.elte.refjava.control/.classpath b/hu.elte.refjava.control/.classpath index e784782..428337e 100644 --- a/hu.elte.refjava.control/.classpath +++ b/hu.elte.refjava.control/.classpath @@ -1,10 +1,6 @@ - - - - - + diff --git a/hu.elte.refjava.control/.settings/org.eclipse.jdt.core.prefs b/hu.elte.refjava.control/.settings/org.eclipse.jdt.core.prefs index 0c68a61..01b2219 100644 --- a/hu.elte.refjava.control/.settings/org.eclipse.jdt.core.prefs +++ b/hu.elte.refjava.control/.settings/org.eclipse.jdt.core.prefs @@ -3,5 +3,8 @@ org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled org.eclipse.jdt.core.compiler.source=1.8 diff --git a/hu.elte.refjava.control/META-INF/MANIFEST.MF b/hu.elte.refjava.control/META-INF/MANIFEST.MF index 4db3b9e..885f6c4 100644 --- a/hu.elte.refjava.control/META-INF/MANIFEST.MF +++ b/hu.elte.refjava.control/META-INF/MANIFEST.MF @@ -17,4 +17,8 @@ Require-Bundle: org.eclipse.ui, org.eclipse.xtext.ui;bundle-version="2.15.0", hu.elte.refjava;bundle-version="1.0.0" Automatic-Module-Name: hu.elte.refjava.control -Bundle-RequiredExecutionEnvironment: JavaSE-10 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Export-Package: hu.elte.refjava.control.handlers, + hu.elte.refjava.control.library, + hu.elte.refjava.control.utils, + hu.elte.refjava.control.wizards diff --git a/hu.elte.refjava.control/src/hu/elte/refjava/control/utils/SelectionNodeFinder.xtend b/hu.elte.refjava.control/src/hu/elte/refjava/control/utils/SelectionNodeFinder.xtend index 13f6270..5c410ae 100644 --- a/hu.elte.refjava.control/src/hu/elte/refjava/control/utils/SelectionNodeFinder.xtend +++ b/hu.elte.refjava.control/src/hu/elte/refjava/control/utils/SelectionNodeFinder.xtend @@ -17,7 +17,7 @@ class SelectionNodeFinder { if (coveringNode == finder.coveredNode) { return #[coveringNode] } - + val selectionRange = Range.closed(selection.offset, selection.offset + selection.length) return coveringNode.children.filter [ val nodeRange = Range.closed(startPosition, startPosition + length) @@ -36,5 +36,4 @@ class SelectionNodeFinder { def private static dispatch getChildren(ASTNode coveringNode) { #[coveringNode] } - } diff --git a/hu.elte.refjava.control/src/hu/elte/refjava/control/utils/TypeDeclarationGetter.xtend b/hu.elte.refjava.control/src/hu/elte/refjava/control/utils/TypeDeclarationGetter.xtend new file mode 100644 index 0000000..3bdecf5 --- /dev/null +++ b/hu.elte.refjava.control/src/hu/elte/refjava/control/utils/TypeDeclarationGetter.xtend @@ -0,0 +1,39 @@ +package hu.elte.refjava.control.utils + +import org.eclipse.core.resources.ResourcesPlugin +import org.eclipse.jdt.core.dom.TypeDeclaration +import java.util.List +import org.eclipse.jdt.core.dom.CompilationUnit +import org.eclipse.jdt.core.JavaCore +import org.eclipse.jdt.core.dom.ASTParser +import org.eclipse.jdt.core.dom.AST + +class TypeDeclarationGetter { + def static getAllTypeDeclarationInWorkspace() { + val workspace = ResourcesPlugin.workspace + val root = workspace.root + val projects = root.projects + + val List allTypeDeclInWorkSpace = newArrayList + for (project : projects) { + val javaProject = JavaCore.create(project) + val packages = javaProject.getPackageFragments() + for (package : packages) { + val iCompUnits = package.compilationUnits + val List allCompUnit = newArrayList + for (iCompUnit : iCompUnits) { + val parser = ASTParser.newParser(AST.JLS12); + parser.resolveBindings = true + parser.source = iCompUnit; + val compUnit = parser.createAST(null) as CompilationUnit; + allCompUnit.add(compUnit) + } + + for (compUnit : allCompUnit) { + allTypeDeclInWorkSpace.addAll(compUnit.types) + } + } + } + allTypeDeclInWorkSpace + } +} \ No newline at end of file diff --git a/hu.elte.refjava.control/src/hu/elte/refjava/control/wizards/RefJavaWizard.xtend b/hu.elte.refjava.control/src/hu/elte/refjava/control/wizards/RefJavaWizard.xtend index e1d464f..784ee96 100644 --- a/hu.elte.refjava.control/src/hu/elte/refjava/control/wizards/RefJavaWizard.xtend +++ b/hu.elte.refjava.control/src/hu/elte/refjava/control/wizards/RefJavaWizard.xtend @@ -2,6 +2,7 @@ package hu.elte.refjava.control.wizards import hu.elte.refjava.api.Refactoring import hu.elte.refjava.control.utils.SelectionNodeFinder +import hu.elte.refjava.control.utils.TypeDeclarationGetter import java.net.URLClassLoader import org.eclipse.jdt.core.ICompilationUnit import org.eclipse.jdt.core.dom.AST @@ -42,25 +43,27 @@ class RefJavaWizard extends Wizard { val editor = PlatformUI.workbench.activeWorkbenchWindow.activePage.activeEditor if (editor instanceof ITextEditor) { - + val selection = editor.selectionProvider.selection if (selection instanceof TextSelection) { - + + val allTypeDeclarationInWorkSpace = TypeDeclarationGetter.allTypeDeclarationInWorkspace + val typeRoot = JavaUI.getEditorInputTypeRoot(editor.editorInput) val iCompUnit = typeRoot.getAdapter(ICompilationUnit) - val parser = ASTParser.newParser(AST.JLS10) + val parser = ASTParser.newParser(AST.JLS12) parser.setResolveBindings(true) parser.source = iCompUnit val compUnit = parser.createAST(null) as CompilationUnit - + val selectedNodes = SelectionNodeFinder.selectedNodes(selection, compUnit) - + val provider = editor.documentProvider val document = provider.getDocument(editor.editorInput) - - refactoringInstance.init(selectedNodes, document) - val status = refactoringInstance.apply + + refactoringInstance.init(selectedNodes, document, allTypeDeclarationInWorkSpace) + val status = refactoringInstance.apply switch status { case SUCCESS: MessageDialog.openInformation(dialog.shell, "Success", @@ -71,7 +74,6 @@ class RefJavaWizard extends Wizard { } } } - return true; } diff --git a/hu.elte.refjava.control/src/hu/elte/refjava/control/wizards/RefJavaWizardPage.xtend b/hu.elte.refjava.control/src/hu/elte/refjava/control/wizards/RefJavaWizardPage.xtend index 3864fa3..283cd83 100644 --- a/hu.elte.refjava.control/src/hu/elte/refjava/control/wizards/RefJavaWizardPage.xtend +++ b/hu.elte.refjava.control/src/hu/elte/refjava/control/wizards/RefJavaWizardPage.xtend @@ -46,7 +46,7 @@ class RefJavaWizardPage extends WizardPage { .flatMap[compilationUnits.toList] .map [ // do not optimize - val parser = ASTParser.newParser(AST.JLS10) + val parser = ASTParser.newParser(AST.JLS12) parser.setResolveBindings(true) parser.source = it parser.createAST(null) as CompilationUnit diff --git a/hu.elte.refjava.examples/.classpath b/hu.elte.refjava.examples/.classpath index 7142ac0..50ec8bc 100644 --- a/hu.elte.refjava.examples/.classpath +++ b/hu.elte.refjava.examples/.classpath @@ -2,8 +2,12 @@ - + + + + + diff --git a/hu.elte.refjava.examples/.settings/org.eclipse.jdt.core.prefs b/hu.elte.refjava.examples/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..34bf367 --- /dev/null +++ b/hu.elte.refjava.examples/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,10 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=12 +org.eclipse.jdt.core.compiler.compliance=12 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=12 diff --git a/hu.elte.refjava.examples/src/hu/elte/refjava/examples/Target.java b/hu.elte.refjava.examples/src/hu/elte/refjava/examples/Target.java index a3e7a32..2ba3684 100644 --- a/hu.elte.refjava.examples/src/hu/elte/refjava/examples/Target.java +++ b/hu.elte.refjava.examples/src/hu/elte/refjava/examples/Target.java @@ -1,18 +1,22 @@ package hu.elte.refjava.examples; -public class Target { - int a; +class A { +} - /* - * Insert a new block after the 3rd statement - * and move the first 3 statements into it. - * Change the 4th 'a' reference to 'this.a' - * and try again the last step. - */ +class B extends A { + int b; + int a; + void f() { - int a = 5; - int b = a; - System.out.println(a + b); - System.out.println(a); + int x = 1; + int a; + a = x; + g(); + int y = 0; + a = y; } -} + + void g() { + a = b = 0; + } +} \ No newline at end of file diff --git a/hu.elte.refjava.examples/src/hu/elte/refjava/examples/refactorings.refjava b/hu.elte.refjava.examples/src/hu/elte/refjava/examples/refactorings.refjava index cab1059..10179d1 100644 --- a/hu.elte.refjava.examples/src/hu/elte/refjava/examples/refactorings.refjava +++ b/hu.elte.refjava.examples/src/hu/elte/refjava/examples/refactorings.refjava @@ -1,20 +1,73 @@ package hu.elte.refjava.examples; -local refactoring identity() - #s.. - ~~~~ - #s.. - -local refactoring insertEmptyBlockAfter() - #s.. - ~~~~~~~~~ - #s.. ; {} - -local refactoring moveIntoNextBlock() - #s1 ; { #s2.. } ; #s3 - ~~~~~~~~~~~~~~~~~~~~~ - { #s1 ; #s2.. } ; #s3 +local refactoring introduceEmptyBlockAfter() + target + ~~~~~~~ + target ; {} + + +block refactoring moveIntoNextBlock() + target ; { #s1.. } ; #s2.. + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + { target ; #s1.. } ; #s2.. +when + precondition + isSingle(target) + && if(isVariableDeclaration(target)) { !isReferencedIn(asVariableDeclaration(target), #s2..) } + else true + + +block refactoring moveToTop() + #s.. ; target + ~~~~~~~~~~~~~ + target ; #s.. +target + type#T name#N + + +lambda refactoring wrapInVoidLambda() + #s.. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + new name#N() { public void apply() { #s.. } }.apply() +when + precondition + !containsValueReturn(#s..) + && if (containsVoidReturn(#s..)) { isLastExecutionNode(getVoidReturn(#s..)) } + else true + + +lambda refactoring lambdaModfification() + new name#N1() { public void apply() { #s.. } }.apply() + ~~~~~~ + new name#N2() { public void apply() { #s.. } }.apply() when - !isVariableDeclaration(#s1) || - !isReferencedIn(asVariableDeclaration(#s1), - blockRemainder(#s3)) + assignment + name#N2 = "newLambda2" + precondition + isFresh(name#N2) + + +class refactoring newMethod() + new name#N() { public void apply(parameter#P..) { #s2.. } }.apply(argument#A..) + ~~~~~~ + newMethod(argument#A..) +definition in class + void newMethod(parameter#P..) { #s2.. } + + +class refactoring liftField() + visibility#V type#T name#N ; + ~~~~~~~ + nothing +definition in super + target + + +class refactoring liftMethod() + visibility#V type#T name#N(parameter#P..) { #s.. } + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + nothing +definition in super + target + + \ No newline at end of file diff --git a/hu.elte.refjava.lang.ide/.classpath b/hu.elte.refjava.lang.ide/.classpath index 8018385..bc001b4 100644 --- a/hu.elte.refjava.lang.ide/.classpath +++ b/hu.elte.refjava.lang.ide/.classpath @@ -1,9 +1,9 @@ + + - - diff --git a/hu.elte.refjava.lang.ide/.settings/org.eclipse.jdt.core.prefs b/hu.elte.refjava.lang.ide/.settings/org.eclipse.jdt.core.prefs index dd5a2bb..3efb014 100644 --- a/hu.elte.refjava.lang.ide/.settings/org.eclipse.jdt.core.prefs +++ b/hu.elte.refjava.lang.ide/.settings/org.eclipse.jdt.core.prefs @@ -9,8 +9,8 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul org.eclipse.jdt.core.compiler.annotation.nullable.secondary= org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=10 -org.eclipse.jdt.core.compiler.compliance=10 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.APILeak=warning org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning org.eclipse.jdt.core.compiler.problem.assertIdentifier=error @@ -22,6 +22,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled org.eclipse.jdt.core.compiler.problem.discouragedReference=ignore org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore @@ -67,6 +68,7 @@ org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=igno org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled @@ -105,4 +107,4 @@ org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=10 +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/hu.elte.refjava.lang.ide/META-INF/MANIFEST.MF b/hu.elte.refjava.lang.ide/META-INF/MANIFEST.MF index eea8fc8..d0f4884 100644 --- a/hu.elte.refjava.lang.ide/META-INF/MANIFEST.MF +++ b/hu.elte.refjava.lang.ide/META-INF/MANIFEST.MF @@ -9,8 +9,12 @@ Bundle-ActivationPolicy: lazy Require-Bundle: hu.elte.refjava, org.eclipse.xtext.ide, org.eclipse.xtext.xbase.ide, - org.antlr.runtime;bundle-version="[3.2.0,3.2.1)" -Bundle-RequiredExecutionEnvironment: JavaSE-10 -Export-Package: hu.elte.refjava.lang.ide.contentassist.antlr, + org.antlr.runtime;bundle-version="[3.2.0,3.2.1)", + com.google.guava, + org.eclipse.xtext.xbase.lib, + org.eclipse.xtend.lib, + org.eclipse.xtend.lib.macro +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Export-Package: hu.elte.refjava.lang.ide, + hu.elte.refjava.lang.ide.contentassist.antlr, hu.elte.refjava.lang.ide.contentassist.antlr.internal - diff --git a/hu.elte.refjava.lang.ide/src/hu/elte/refjava/lang/ide/RefJavaIdeSetup.xtend b/hu.elte.refjava.lang.ide/src/hu/elte/refjava/lang/ide/RefJavaIdeSetup.xtend index 4899d68..676cd03 100644 --- a/hu.elte.refjava.lang.ide/src/hu/elte/refjava/lang/ide/RefJavaIdeSetup.xtend +++ b/hu.elte.refjava.lang.ide/src/hu/elte/refjava/lang/ide/RefJavaIdeSetup.xtend @@ -16,5 +16,4 @@ class RefJavaIdeSetup extends RefJavaStandaloneSetup { override createInjector() { Guice.createInjector(Modules2.mixin(new RefJavaRuntimeModule, new RefJavaIdeModule)) } - } diff --git a/hu.elte.refjava.lang.tests/.classpath b/hu.elte.refjava.lang.tests/.classpath new file mode 100644 index 0000000..6b3cdb9 --- /dev/null +++ b/hu.elte.refjava.lang.tests/.classpath @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/hu.elte.refjava.lang.tests/.gitignore b/hu.elte.refjava.lang.tests/.gitignore new file mode 100644 index 0000000..b778aa2 --- /dev/null +++ b/hu.elte.refjava.lang.tests/.gitignore @@ -0,0 +1,4 @@ +bin/ +bin-test/ +src-gen/ +xtend-gen/ diff --git a/hu.elte.refjava.lang.tests/.project b/hu.elte.refjava.lang.tests/.project new file mode 100644 index 0000000..a1eab17 --- /dev/null +++ b/hu.elte.refjava.lang.tests/.project @@ -0,0 +1,34 @@ + + + hu.elte.refjava.lang.tests + + + + + + org.eclipse.xtext.ui.shared.xtextBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.xtext.ui.shared.xtextNature + org.eclipse.jdt.core.javanature + org.eclipse.pde.PluginNature + + diff --git a/hu.elte.refjava.lang.tests/.settings/org.eclipse.core.resources.prefs b/hu.elte.refjava.lang.tests/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..fdaa299 --- /dev/null +++ b/hu.elte.refjava.lang.tests/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=windows-1250 diff --git a/hu.elte.refjava.lang.tests/.settings/org.eclipse.jdt.core.prefs b/hu.elte.refjava.lang.tests/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..e50443c --- /dev/null +++ b/hu.elte.refjava.lang.tests/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,15 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/hu.elte.refjava.lang.tests/.settings/org.eclipse.ltk.core.refactoring.prefs b/hu.elte.refjava.lang.tests/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 0000000..b196c64 --- /dev/null +++ b/hu.elte.refjava.lang.tests/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/hu.elte.refjava.lang.tests/META-INF/MANIFEST.MF b/hu.elte.refjava.lang.tests/META-INF/MANIFEST.MF new file mode 100644 index 0000000..59ac254 --- /dev/null +++ b/hu.elte.refjava.lang.tests/META-INF/MANIFEST.MF @@ -0,0 +1,31 @@ +Manifest-Version: 1.0 +Automatic-Module-Name: hu.elte.refjava.lang.tests +Bundle-ManifestVersion: 2 +Bundle-Name: hu.elte.refjava.lang.tests +Bundle-Vendor: My Company +Bundle-Version: 1.0.0.qualifier +Bundle-SymbolicName: hu.elte.refjava.lang.tests;singleton:=true +Bundle-ActivationPolicy: lazy +Require-Bundle: hu.elte.refjava, + org.junit.jupiter.api;bundle-version="[5.0.0,6.0.0)", + org.eclipse.xtext.testing, + org.eclipse.xtext.xbase.testing, + org.objectweb.asm;bundle-version="[7.1.0,7.2.0)";resolution:=optional, + org.eclipse.jdt.core;bundle-version="3.19.0", + org.eclipse.xtext.xbase.lib;bundle-version="2.14.0", + com.google.guava, + org.eclipse.xtext.xbase.lib;bundle-version="2.14.0", + org.eclipse.xtend.lib, + org.eclipse.xtend.lib.macro, + org.eclipse.ui.editors;bundle-version="3.11.200", + org.eclipse.jface.text;bundle-version="3.14.0", + org.eclipse.core.runtime;bundle-version="3.15.0", + org.eclipse.jdt.core;bundle-version="3.15.0", + org.eclipse.jdt.ui;bundle-version="3.15.0", + org.eclipse.core.resources;bundle-version="3.13.100", + org.eclipse.xtext.ui;bundle-version="2.15.0" +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Export-Package: hu.elte.refjava.lang.tests;x-internal=true, + hu.elte.refjava.lang.tests.api, + hu.elte.refjava.lang.tests.compiler, + hu.elte.refjava.lang.tests.parser diff --git a/hu.elte.refjava.lang.tests/build.properties b/hu.elte.refjava.lang.tests/build.properties new file mode 100644 index 0000000..f8b77a8 --- /dev/null +++ b/hu.elte.refjava.lang.tests/build.properties @@ -0,0 +1,7 @@ +source.. = src/,\ + src-gen/,\ + xtend-gen/ +bin.includes = .,\ + META-INF/ +bin.excludes = **/*.xtend +jars.compile.order = . diff --git a/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/RefJavaParsingTest.xtend b/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/RefJavaParsingTest.xtend new file mode 100644 index 0000000..5489dda --- /dev/null +++ b/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/RefJavaParsingTest.xtend @@ -0,0 +1,14 @@ +/* + * generated by Xtext + */ +package hu.elte.refjava.lang.tests + +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.^extension.ExtendWith + +@ExtendWith(InjectionExtension) +@InjectWith(RefJavaInjectorProvider) +class RefJavaParsingTest { + +} diff --git a/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/api/ASTBuilderTests.xtend b/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/api/ASTBuilderTests.xtend new file mode 100644 index 0000000..18643bf --- /dev/null +++ b/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/api/ASTBuilderTests.xtend @@ -0,0 +1,182 @@ +package hu.elte.refjava.lang.tests.api + +import hu.elte.refjava.lang.refJava.Visibility +import hu.elte.refjava.lang.tests.RefJavaInjectorProvider +import java.util.List +import java.util.Map +import org.eclipse.jdt.core.dom.ASTNode +import org.eclipse.jdt.core.dom.CompilationUnit +import org.eclipse.jdt.core.dom.Expression +import org.eclipse.jdt.core.dom.ExpressionStatement +import org.eclipse.jdt.core.dom.FieldDeclaration +import org.eclipse.jdt.core.dom.MethodDeclaration +import org.eclipse.jdt.core.dom.MethodInvocation +import org.eclipse.jdt.core.dom.SingleVariableDeclaration +import org.eclipse.jdt.core.dom.Type +import org.eclipse.jdt.core.dom.TypeDeclaration +import org.eclipse.jdt.core.dom.VariableDeclarationStatement +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith + +import static org.junit.jupiter.api.Assertions.* + +@ExtendWith(InjectionExtension) +@InjectWith(RefJavaInjectorProvider) +package class ASTBuilderTests { + + Map> bindings = newHashMap + Map nameBindings = newHashMap + Map typeBindings = newHashMap + Map> parameterBindings = newHashMap + Map visibilityBindings = newHashMap + Map> argumentBindings = newHashMap + String typeRefString = null + + List replacement + CompilationUnit source + + @Test + def void variableDeclarationBuilderTest() { + + source = TestUtils.getCompliationUnit('''class A { public void f() { int a; char b; String str; } }''') + val sourceVariableDeclarations = ((source.types.head as TypeDeclaration).bodyDeclarations.head as MethodDeclaration).body.statements.map[it as VariableDeclarationStatement] + var List replacementVariableDeclarations + + // #1 + typeRefString = "void|int|char|java.lang.String|" + replacement = TestUtils.testBuilder('''public void f() { int a ; char b ; String str }''', + bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + assertTrue(replacement.head instanceof MethodDeclaration) + replacementVariableDeclarations = (replacement.head as MethodDeclaration).body.statements + for(var int i = 0; i < replacementVariableDeclarations.size; i++) { + assertTrue(replacementVariableDeclarations.get(i) instanceof VariableDeclarationStatement) + assertEquals(replacementVariableDeclarations.get(i).toString, sourceVariableDeclarations.get(i).toString) + } + + // #2 + typeRefString = "void|" + nameBindings.put("N1", "a") + nameBindings.put("N2", "b") + nameBindings.put("N3", "str") + typeBindings.put("T1", sourceVariableDeclarations.get(0).type) + typeBindings.put("T2", sourceVariableDeclarations.get(1).type) + typeBindings.put("T3", sourceVariableDeclarations.get(2).type) + replacement = TestUtils.testBuilder('''public void f() { type#T1 name#N1 ; type#T2 name#N2 ; type#T3 name#N3 }''', + bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + assertTrue(replacement.head instanceof MethodDeclaration) + replacementVariableDeclarations = (replacement.head as MethodDeclaration).body.statements + for(var int i = 0; i < replacementVariableDeclarations.size; i++) { + assertTrue(replacementVariableDeclarations.get(i) instanceof VariableDeclarationStatement) + assertEquals(sourceVariableDeclarations.get(i).toString, replacementVariableDeclarations.get(i).toString) + } + } + + @Test + def void fieldDeclarationBuilderTest() { + + source = TestUtils.getCompliationUnit('''class A { public int a; private char b; String str }''') + val sourceFieldDeclarations = (source.types.head as TypeDeclaration).bodyDeclarations.map[it as FieldDeclaration] + var List replacementFieldDeclarations + + // #1 + typeRefString = "int|char|java.lang.String|" + replacement = TestUtils.testBuilder('''public int a ; private char b ; String str''', + bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + assertTrue(replacement.forall[it instanceof FieldDeclaration]) + replacementFieldDeclarations = replacement.map[it as FieldDeclaration] + for(var int i = 0; i < replacementFieldDeclarations.size; i++) { + assertEquals(sourceFieldDeclarations.get(i).toString, replacementFieldDeclarations.get(i).toString) + } + + // #2 + typeRefString = null + nameBindings.put("N1", "a") + nameBindings.put("N2", "b") + nameBindings.put("N3", "str") + typeBindings.put("T1", sourceFieldDeclarations.get(0).type) + typeBindings.put("T2", sourceFieldDeclarations.get(1).type) + typeBindings.put("T3", sourceFieldDeclarations.get(2).type) + visibilityBindings.put("V1", Visibility.PUBLIC) + visibilityBindings.put("V2", Visibility.PRIVATE) + visibilityBindings.put("V3", Visibility.PACKAGE) + replacement = TestUtils.testBuilder('''visibility#V1 type#T1 name#N1 ; visibility#V2 type#T2 name#N2 ; visibility#V3 type#T3 name#N3''', + bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + assertTrue(replacement.forall[it instanceof FieldDeclaration]) + replacementFieldDeclarations = replacement.map[it as FieldDeclaration] + for(var int i = 0; i < replacementFieldDeclarations.size; i++) { + assertEquals(sourceFieldDeclarations.get(i).toString, replacementFieldDeclarations.get(i).toString) + } + } + + @Test + def void methodDeclarationBuilderTest() { + + source = TestUtils.getCompliationUnit('''class A { public void f(int a){ } private short g(boolean l, char b){ } String h(){ } }''') + val sourceMethodDeclarations = (source.types.head as TypeDeclaration).bodyDeclarations.map[it as MethodDeclaration] + var List replacementMethodDeclarations + + // #1 + typeRefString = "void|int|short|boolean|char|java.lang.String|" + replacement = TestUtils.testBuilder('''public void f(int a){ } ; private short g(boolean l, char b){ } ; String h(){ }''', + bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + assertTrue(replacement.forall[it instanceof MethodDeclaration]) + replacementMethodDeclarations = replacement.map[it as MethodDeclaration] + for(var int i = 0; i < replacementMethodDeclarations.size; i++) { + assertEquals(sourceMethodDeclarations.get(i).toString, replacementMethodDeclarations.get(i).toString) + } + + // #2 + typeRefString = null + nameBindings.put("N1", "f") + nameBindings.put("N2", "g") + nameBindings.put("N3", "h") + typeBindings.put("T1", sourceMethodDeclarations.get(0).returnType2) + typeBindings.put("T2", sourceMethodDeclarations.get(1).returnType2) + typeBindings.put("T3", sourceMethodDeclarations.get(2).returnType2) + visibilityBindings.put("V1", Visibility.PUBLIC) + visibilityBindings.put("V2", Visibility.PRIVATE) + visibilityBindings.put("V3", Visibility.PACKAGE) + parameterBindings.put("P1", sourceMethodDeclarations.get(0).parameters) + parameterBindings.put("P2", sourceMethodDeclarations.get(1).parameters) + parameterBindings.put("P3", sourceMethodDeclarations.get(2).parameters) + replacement = TestUtils.testBuilder('''visibility#V1 type#T1 name#N1(parameter#P1..){ } ; visibility#V2 type#T2 name#N2(parameter#P2..){ } ; visibility#V3 type#T3 name#N3(parameter#P3..){ }''', + bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + assertTrue(replacement.forall[it instanceof MethodDeclaration]) + replacementMethodDeclarations = replacement.map[it as MethodDeclaration] + for(var int i = 0; i < replacementMethodDeclarations.size; i++) { + assertEquals(sourceMethodDeclarations.get(i).toString, replacementMethodDeclarations.get(i).toString) + } + } + + @Test + def void methodInvocetionAndConstructorCallBuilderTest() { + + source = TestUtils.getCompliationUnit('''class A { public void f() { new F() { public void apply(int a, char b) {} }.apply(a, b); new G() { public void apply() {} }.apply(); } int a = 1; char b = 'a'; }''') + val sourceMethodInvocations = ((source.types.head as TypeDeclaration).bodyDeclarations.head as MethodDeclaration).body.statements.map[(it as ExpressionStatement).expression as MethodInvocation] + var List replacementMethodInvocations + + // #1 + typeRefString = "void|int|char|void|" + argumentBindings.put("A1", sourceMethodInvocations.get(0).arguments) + replacement = TestUtils.testBuilder('''new F() { public void apply(int a, char b) {} }.apply(argument#A1..) ; new G() { public void apply() {} }.apply()''', + bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + assertTrue(replacement.forall[it instanceof ExpressionStatement] && replacement.forall[(it as ExpressionStatement).expression instanceof MethodInvocation]) + replacementMethodInvocations = replacement.map[(it as ExpressionStatement).expression as MethodInvocation] + for(var int i = 0; i < replacementMethodInvocations.size; i++) { + assertEquals(sourceMethodInvocations.get(i).toString, replacementMethodInvocations.get(i).toString) + } + + // #2 + nameBindings.put("N1", "F") + nameBindings.put("N2", "G") + replacement = TestUtils.testBuilder('''new name#N1() { public void apply(int a, char b) {} }.apply(argument#A1..) ; new name#N2() { public void apply() {} }.apply()''', + bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + assertTrue(replacement.forall[it instanceof ExpressionStatement] && replacement.forall[(it as ExpressionStatement).expression instanceof MethodInvocation]) + replacementMethodInvocations = replacement.map[(it as ExpressionStatement).expression as MethodInvocation] + for(var int i = 0; i < replacementMethodInvocations.size; i++) { + assertEquals(sourceMethodInvocations.get(i).toString, replacementMethodInvocations.get(i).toString) + } + } +} \ No newline at end of file diff --git a/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/api/PatternMatcherTests.xtend b/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/api/PatternMatcherTests.xtend new file mode 100644 index 0000000..c72fccc --- /dev/null +++ b/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/api/PatternMatcherTests.xtend @@ -0,0 +1,242 @@ +package hu.elte.refjava.lang.tests.api + +import hu.elte.refjava.lang.refJava.Visibility +import hu.elte.refjava.lang.tests.RefJavaInjectorProvider +import java.util.List +import java.util.Map +import org.eclipse.jdt.core.dom.ClassInstanceCreation +import org.eclipse.jdt.core.dom.Expression +import org.eclipse.jdt.core.dom.ExpressionStatement +import org.eclipse.jdt.core.dom.FieldDeclaration +import org.eclipse.jdt.core.dom.MethodDeclaration +import org.eclipse.jdt.core.dom.MethodInvocation +import org.eclipse.jdt.core.dom.SingleVariableDeclaration +import org.eclipse.jdt.core.dom.Type +import org.eclipse.jdt.core.dom.TypeDeclaration +import org.eclipse.jdt.core.dom.VariableDeclarationStatement +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith + +@ExtendWith(InjectionExtension) +@InjectWith(RefJavaInjectorProvider) +package class PatternMatcherTests { + + Map nameBindings = newHashMap + Map typeBindings = newHashMap + Map> parameterBindings = newHashMap + Map visibilityBindings = newHashMap + Map> argumentBindings = newHashMap + String typeRefString = null + + @Test + def void variableDeclarationMatcherTest() { + // #1 + TestUtils.testMatcher('''type#T1 name#N1 ; type#T2 name#N2 ;''', + ''' + class A { + void f(){ + int a; + char b; + } + } + ''', + "block", + nameBindings , typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + + // #2 + val compUnit = TestUtils.getCompliationUnit("class A { void f(){ int a; char b; } }") + val methodBody = ((compUnit.types.head as TypeDeclaration).bodyDeclarations.head as MethodDeclaration).body.statements + nameBindings.put("N1", "a") + nameBindings.put("N2", "b") + typeBindings.put("T1", (methodBody.head as VariableDeclarationStatement).type ) + typeBindings.put("T2", (methodBody.last as VariableDeclarationStatement).type ) + TestUtils.testMatcher('''type#T1 name#N1 ; type#T2 name#N2 ;''', + ''' + class A { + void f(){ + int a; + char b; + } + } + ''', + "block", + nameBindings , typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + + // #3 + typeRefString="int|char|java.lang.String|" + TestUtils.testMatcher('''int a ; char b ; String c ;''', + ''' + class A { + void f(){ + int a; + char b; + String c; + } + } + ''', + "block", + nameBindings , typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + } + + @Test + def void fieldDeclarationMatcherTest() { + // #1 + TestUtils.testMatcher('''visibility#V1 type#T1 name#N1 ; visibility#V2 type#T2 name#N2 ;''', + ''' + class A { + public int a; + private char b; + } + ''', + "class", + nameBindings , typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + + // #2 + val compUnit = TestUtils.getCompliationUnit("class A { public int a; private char b; }") + val fieldDeclarations = (compUnit.types.head as TypeDeclaration).bodyDeclarations + nameBindings.put("N1", "a") + nameBindings.put("N2", "b") + typeBindings.put("T1", (fieldDeclarations.head as FieldDeclaration).type ) + typeBindings.put("T2", (fieldDeclarations.last as FieldDeclaration).type ) + visibilityBindings.put("V1", Visibility.PUBLIC) + visibilityBindings.put("V2", Visibility.PRIVATE) + TestUtils.testMatcher('''visibility#V1 type#T1 name#N1 ; visibility#V2 type#T2 name#N2 ;''', + ''' + class A { + public int a ; + private char b; + } + ''', + "class", + nameBindings , typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + + // #3 + typeRefString = "int|char|" + TestUtils.testMatcher('''public int a ; private char b ;''', + ''' + class A { + public int a ; + private char b; + } + ''', + "class", + nameBindings , typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + } + + @Test + def void methodDeclarationMatcherTest() { + // #1 + TestUtils.testMatcher('''visibility#V1 type#T1 name#N1(parameter#P1..) {} ; visibility#V2 type#T2 name#N2(parameter#P2..) {} ;''', + ''' + class A { + public void f(int a, String str) {} + private int g() {} + } + ''', + "class", + nameBindings , typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + + // #2 + val compUnit = TestUtils.getCompliationUnit("class A { public void f(int a, String str) {} private int g() {} }") + val methodDeclarations = (compUnit.types.head as TypeDeclaration).bodyDeclarations + nameBindings.put("N1", "f") + nameBindings.put("N2", "g") + typeBindings.put("T1", (methodDeclarations.head as MethodDeclaration).returnType2 ) + typeBindings.put("T2", (methodDeclarations.last as MethodDeclaration).returnType2 ) + visibilityBindings.put("V1", Visibility.PUBLIC) + visibilityBindings.put("V2", Visibility.PRIVATE) + parameterBindings.put("P1", (methodDeclarations.head as MethodDeclaration).parameters) + parameterBindings.put("P2", (methodDeclarations.last as MethodDeclaration).parameters) + + TestUtils.testMatcher('''visibility#V1 type#T1 name#N1(parameter#P1..) {} ; visibility#V2 type#T2 name#N2(parameter#P2..) {} ;''', + ''' + class A { + public void f(int a, String str) {} + private int g() {} + } + ''', + "class", + nameBindings , typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + + // #3 + typeRefString = "void|int|java.lang.String|int|" + TestUtils.testMatcher('''public void f(int a, String str) {} ; private int g() {} ;''', + ''' + class A { + public void f(int a, String str) {} + private int g() {} + } + ''', + "class", + nameBindings , typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + } + + @Test + def void methodInvocationAndConstructorCallMatcherTest() { + // #1 + TestUtils.testMatcher('''new name#N1() { visibility#V1 type#T1 name#N2(parameter#P1..) {} }.name#N2(argument#A1..)''', + ''' + class A { + void f() { + new F() { + public void apply(int a) {} + }.apply(a); + } + public int a = 1; + } + ''', + "block", + nameBindings , typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + + // #2 + val compUnit = TestUtils.getCompliationUnit("class A { public void f() { new F() { public void apply(int a, char b) {} }.apply(a, b); } int a = 1; char b = 'a'; }") + val methodInvocation = (((compUnit.types.head as TypeDeclaration).bodyDeclarations.head as MethodDeclaration).body.statements.head as ExpressionStatement).expression as MethodInvocation + val method = (((((compUnit.types.head as TypeDeclaration).bodyDeclarations.head as MethodDeclaration).body.statements.head as ExpressionStatement) + .expression as MethodInvocation) + .expression as ClassInstanceCreation) + .anonymousClassDeclaration.bodyDeclarations.head as MethodDeclaration + nameBindings.put("N1", "F") + nameBindings.put("N2", "apply") + typeBindings.put("T1", method.returnType2) + visibilityBindings.put("V1", Visibility.PUBLIC) + parameterBindings.put("P1", method.parameters) + argumentBindings.put("A1", methodInvocation.arguments) + TestUtils.testMatcher('''new name#N1() { visibility#V1 type#T1 name#N2(parameter#P1..) {} }.name#N2(argument#A1..)''', + ''' + class A { + void f() { + new F() { + public void apply(int a, char b) {} + }.apply(a, b); + } + int a = 1; + char b = 'a'; + } + ''', + "block", + nameBindings , typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + + // #3 + typeRefString = "void|int|char|" + TestUtils.testMatcher('''new F() { public void apply(int a, char b) {} }.apply(argument#A1..)''', + ''' + class A { + void f() { + new F() { + public void apply(int a, char b) {} + }.apply(a, b); + } + } + int a = 1; + char b = 'a'; + ''', + "block", + nameBindings , typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + } + + + + +} \ No newline at end of file diff --git a/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/api/TestUtils.xtend b/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/api/TestUtils.xtend new file mode 100644 index 0000000..6217535 --- /dev/null +++ b/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/api/TestUtils.xtend @@ -0,0 +1,52 @@ +package hu.elte.refjava.lang.tests.api + +import hu.elte.refjava.api.patterns.ASTBuilder +import hu.elte.refjava.api.patterns.PatternMatcher +import hu.elte.refjava.api.patterns.PatternParser +import hu.elte.refjava.lang.refJava.Visibility +import java.util.List +import java.util.Map +import org.eclipse.jdt.core.dom.AST +import org.eclipse.jdt.core.dom.ASTParser +import org.eclipse.jdt.core.dom.CompilationUnit +import org.eclipse.jdt.core.dom.Expression +import org.eclipse.jdt.core.dom.MethodDeclaration +import org.eclipse.jdt.core.dom.SingleVariableDeclaration +import org.eclipse.jdt.core.dom.Type +import org.eclipse.jdt.core.dom.TypeDeclaration + +import static org.junit.jupiter.api.Assertions.* +import org.eclipse.jdt.core.dom.ASTNode + +class TestUtils { + + def static getCompliationUnit(String str) { + val parser = ASTParser.newParser(AST.JLS12); + parser.setUnitName("test.java"); + parser.setEnvironment(null, null, null, true); + parser.resolveBindings = true + parser.source = str.toCharArray + val newCompUnit = parser.createAST(null) as CompilationUnit + newCompUnit + } + + def static void testMatcher(String patternString, String sourceString, String declarationSource, Map nameBindings, Map typeBindings, Map> parameterBindings, Map visibilityBindings, Map> argumentBindings, String typeRefString) { + val matcher = new PatternMatcher(null) + val pattern = PatternParser.parse(patternString) + val source = TestUtils.getCompliationUnit(sourceString) + val matchings = if(declarationSource == "block") { + ((source.types.head as TypeDeclaration).bodyDeclarations.head as MethodDeclaration).body.statements + } else if(declarationSource == "class") { + (source.types.head as TypeDeclaration).bodyDeclarations + } + assertTrue(matcher.match(pattern, matchings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString)) + } + + def static testBuilder(String patternString, Map> bindings, Map nameBindings, Map typeBindings, Map> parameterBindings, Map visibilityBindings, Map> argumentBindings, String typeRefString) { + val builder = new ASTBuilder(null) + val pattern = PatternParser.parse(patternString) + val source = TestUtils.getCompliationUnit("") + builder.build(pattern, source.AST, bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + } + +} \ No newline at end of file diff --git a/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/compiler/RefJavaCompilerTests.xtend b/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/compiler/RefJavaCompilerTests.xtend new file mode 100644 index 0000000..b47ee03 --- /dev/null +++ b/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/compiler/RefJavaCompilerTests.xtend @@ -0,0 +1,297 @@ +package hu.elte.refjava.lang.tests.compiler + +import org.junit.jupiter.api.^extension.ExtendWith +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import hu.elte.refjava.lang.tests.RefJavaInjectorProvider +import com.google.inject.Inject +import org.eclipse.xtext.xbase.testing.CompilationTestHelper +import org.junit.jupiter.api.Test + +@ExtendWith(InjectionExtension) +@InjectWith(RefJavaInjectorProvider) +class RefJavaCompilerTests { + + @Inject extension CompilationTestHelper + + @Test + def void compileSchemeTypes() { + ''' + package file.test; + + local refactoring localTest() + nothing + ~~~~~~~ + nothing + + block refactoring blockTest() + nothing + ~~~~~~~ + nothing + + lambda refactoring lambdaTest() + nothing + ~~~~~~~ + nothing + + class refactoring classTest() + nothing + ~~~~~~~ + nothing + '''.assertCompilesTo(''' + MULTIPLE FILES WERE GENERATED + + File 1 : /myProject/./src-gen/file/test/blockTest.java + + package file.test; + + import hu.elte.refjava.api.BlockRefactoring; + + @SuppressWarnings("all") + public class blockTest extends BlockRefactoring { + public blockTest() { + super("nothing", "nothing"); + } + } + + File 2 : /myProject/./src-gen/file/test/classTest.java + + package file.test; + + import hu.elte.refjava.api.ClassRefactoring; + + @SuppressWarnings("all") + public class classTest extends ClassRefactoring { + public classTest() { + super("nothing", "nothing"); + } + } + + File 3 : /myProject/./src-gen/file/test/lambdaTest.java + + package file.test; + + import hu.elte.refjava.api.LambdaRefactoring; + + @SuppressWarnings("all") + public class lambdaTest extends LambdaRefactoring { + public lambdaTest() { + super("nothing", "nothing"); + } + } + + File 4 : /myProject/./src-gen/file/test/localTest.java + + package file.test; + + import hu.elte.refjava.api.LocalRefactoring; + + @SuppressWarnings("all") + public class localTest extends LocalRefactoring { + public localTest() { + super("nothing", "nothing"); + } + } + + ''') + } + + @Test + def void compileSchemeWithTargetClosure() { + ''' + package test; + + block refactoring test() + nothing + ~~~~~~~ + nothing + target + nothing + ''' + .assertCompilesTo( + ''' + package test; + + import hu.elte.refjava.api.BlockRefactoring; + + @SuppressWarnings("all") + public class test extends BlockRefactoring { + public test() { + super("nothing", "nothing"); + } + + @Override + protected boolean safeTargetCheck() { + return super.targetCheck("nothing"); + } + } + ''') + } + + @Test + def void compileSchemeWithPrecondition() { + ''' + package test; + + local refactoring test() + nothing + ~~~~~~~ + nothing + when + precondition + isSingle(target) + && true == true + ''' + .assertCompilesTo( + ''' + package test; + + import hu.elte.refjava.api.Check; + import hu.elte.refjava.api.LocalRefactoring; + + @SuppressWarnings("all") + public class test extends LocalRefactoring { + public test() { + super("nothing", "nothing"); + } + + private boolean instanceCheck() { + return (Check.isSingle(bindings.get("target")) && (true == true)); + } + + @Override + protected boolean check() { + return super.check() && instanceCheck(); + } + } + ''') + } + + @Test + def void compileSchemeWithAssignments() { + ''' + package test; + + local refactoring test() + nothing + ~~~~~~~ + nothing + when + assignment + name#N = "TEST" ; + type#T = type(target) ; + visibility#V = visibility(target) ; + parameter#P = parameters(target) + + ''' + .assertCompilesTo( + ''' + package test; + + import hu.elte.refjava.api.Check; + import hu.elte.refjava.api.LocalRefactoring; + import hu.elte.refjava.lang.refJava.Visibility; + import java.util.List; + import org.eclipse.jdt.core.dom.SingleVariableDeclaration; + import org.eclipse.jdt.core.dom.Type; + + @SuppressWarnings("all") + public class test extends LocalRefactoring { + public test() { + super("nothing", "nothing"); + } + + private String valueof_name_N() { + return "TEST"; + } + + private void set_name_N() { + nameBindings.put("N", valueof_name_N()); + } + + private Type valueof_type_T() { + Type _type = Check.type(bindings.get("target")); + return _type; + } + + private void set_type_T() { + typeBindings.put("T", valueof_type_T()); + } + + private List valueof_visibility_V() { + Visibility _visibility = Check.visibility(bindings.get("target")); + return _visibility; + } + + private void set_visibility_V() { + visibilityBindings.put("V", valueof_visibility_V()); + } + + private List valueof_parameter_P() { + List _parameters = Check.parameters(bindings.get("target")); + return _parameters; + } + + private void set_parameter_P() { + parameterBindings.put("P", valueof_parameter_P()); + } + + protected void setMetaVariables() { + set_name_N(); + set_type_T(); + set_visibility_V(); + set_parameter_P(); + + super.matchingTypeReferenceString = ""; + super.replacementTypeReferenceString = ""; + super.targetTypeReferenceString = ""; + super.definitionTypeReferenceString = ""; + } + } + ''') + } + + @Test + def void compileSchemeWithTypeRefStrings(){ + ''' + package test; + + class refactoring test() + int a ; char b ; + ~~~~~~~~~~~~~~ + double c ; long d ; String e ; + target + public void f(boolean b) { } + definition + byte b ; short s ; + + ''' + .assertCompilesTo( + ''' + package test; + + import hu.elte.refjava.api.ClassRefactoring; + + @SuppressWarnings("all") + public class test extends ClassRefactoring { + public test() { + super("int a ; char b ;", "double c ; long d ; String e ;"); + } + + protected void setMetaVariables() { + super.definitionString = "byte b ; short s ;"; + + super.matchingTypeReferenceString = "int|char|"; + super.replacementTypeReferenceString = "double|long|java.lang.String|"; + super.targetTypeReferenceString = "void|boolean|"; + super.definitionTypeReferenceString = "byte|short|"; + } + + @Override + protected boolean safeTargetCheck() { + return super.targetCheck("public void f(boolean b) { }"); + } + } + ''') + } +} \ No newline at end of file diff --git a/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/parser/RefJavaParsingTests.xtend b/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/parser/RefJavaParsingTests.xtend new file mode 100644 index 0000000..ac56b10 --- /dev/null +++ b/hu.elte.refjava.lang.tests/src/hu/elte/refjava/lang/tests/parser/RefJavaParsingTests.xtend @@ -0,0 +1,164 @@ +package hu.elte.refjava.lang.tests.parser + +import com.google.inject.Inject +import hu.elte.refjava.lang.refJava.AssignmentList +import hu.elte.refjava.lang.refJava.File +import hu.elte.refjava.lang.refJava.MetaVariableType +import hu.elte.refjava.lang.refJava.PBlockExpression +import hu.elte.refjava.lang.refJava.PConstructorCall +import hu.elte.refjava.lang.refJava.PExpression +import hu.elte.refjava.lang.refJava.PFeatureCall +import hu.elte.refjava.lang.refJava.PMemberFeatureCall +import hu.elte.refjava.lang.refJava.PMetaVariable +import hu.elte.refjava.lang.refJava.PMethodDeclaration +import hu.elte.refjava.lang.refJava.PNothingExpression +import hu.elte.refjava.lang.refJava.PReturnExpression +import hu.elte.refjava.lang.refJava.PTargetExpression +import hu.elte.refjava.lang.refJava.PVariableDeclaration +import hu.elte.refjava.lang.refJava.Pattern +import hu.elte.refjava.lang.refJava.SchemeInstanceRule +import hu.elte.refjava.lang.refJava.SchemeType +import hu.elte.refjava.lang.tests.RefJavaInjectorProvider +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.eclipse.xtext.testing.util.ParseHelper +import org.eclipse.xtext.xbase.XExpression +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith + +import static org.junit.jupiter.api.Assertions.* + +@ExtendWith(InjectionExtension) +@InjectWith(RefJavaInjectorProvider) +class RefJavaParsingTests { + + @Inject extension ParseHelper parseHelper + + @Test + def void parseFile() { + val file = ''' + package file.test; + '''.parse + + assertTrue(file instanceof File) + assertEquals(file.name, "file.test") + } + + @Test + def void parseAllSchemeTypes() { + val file = ''' + package file.test; + local refactoring localTest() + nothing + ~~~~~~~ + nothing + + block refactoring blockTest() + nothing + ~~~~~~~ + nothing + + lambda refactoring lambdaTest() + nothing + ~~~~~~~ + nothing + + class refactoring classTest() + nothing + ~~~~~~~ + nothing + '''.parse + + assertTrue(file.refactorings.forall[it instanceof SchemeInstanceRule]) + assertEquals((file.refactorings.get(0) as SchemeInstanceRule).type, SchemeType.LOCAL) + assertEquals((file.refactorings.get(0) as SchemeInstanceRule).name, "localTest") + assertEquals((file.refactorings.get(1) as SchemeInstanceRule).type, SchemeType.BLOCK) + assertEquals((file.refactorings.get(1) as SchemeInstanceRule).name, "blockTest") + assertEquals((file.refactorings.get(2) as SchemeInstanceRule).type, SchemeType.LAMBDA) + assertEquals((file.refactorings.get(2) as SchemeInstanceRule).name, "lambdaTest") + assertEquals((file.refactorings.get(3) as SchemeInstanceRule).type, SchemeType.CLASS) + assertEquals((file.refactorings.get(3) as SchemeInstanceRule).name, "classTest") + } + + + @Test + def void parseSchemeProperties() { + val file = ''' + package file.test; + local refactoring test() + nothing + ~~~~~~~ + nothing + target + nothing + definition + nothing + when + assignment + name#test = "TEST" + precondition + true + '''.parse + + val refactoring = file.refactorings.head as SchemeInstanceRule + + assertTrue(refactoring.matchingPattern instanceof Pattern) + assertTrue(refactoring.replacementPattern instanceof Pattern) + assertFalse(refactoring.targetPattern === null) + assertTrue(refactoring.targetPattern instanceof Pattern) + assertFalse(refactoring.definitionPattern === null) + assertTrue(refactoring.definitionPattern instanceof Pattern) + assertFalse(refactoring.assignments === null) + assertTrue(refactoring.assignments instanceof AssignmentList) + assertFalse(refactoring.precondition === null) + assertTrue(refactoring.precondition instanceof XExpression) + } + + @Test + def void parsePatternExpressions() { + val file = ''' + package file.test; + local refactoring test() + #s ; target ; return ; nothing ; { } ; public int a ; public void f() { } ; method() ; A.method() ; new F() { } + ~~~~~~~ + nothing + '''.parse + + val pattern = (file.refactorings.head as SchemeInstanceRule).matchingPattern + assertFalse(pattern.patterns === null) + assertTrue(pattern.patterns.forall[it instanceof PExpression]) + + val patterns = pattern.patterns + assertTrue(patterns.get(0) instanceof PMetaVariable) + assertTrue(patterns.get(1) instanceof PTargetExpression) + assertTrue(patterns.get(2) instanceof PReturnExpression) + assertTrue(patterns.get(3) instanceof PNothingExpression) + assertTrue(patterns.get(4) instanceof PBlockExpression) + assertTrue(patterns.get(5) instanceof PVariableDeclaration) + assertTrue(patterns.get(6) instanceof PMethodDeclaration) + assertTrue(patterns.get(7) instanceof PFeatureCall) + assertTrue(patterns.get(8) instanceof PMemberFeatureCall) + assertTrue(patterns.get(9) instanceof PConstructorCall) + } + + @Test + def void parseMetaVariables() { + val file = ''' + package file.test; + local refactoring test() + #s ; name#n ; type#t ; visibility#v ; argument#a.. ; parameter#p.. + ~~~~~~~ + nothing + '''.parse + + val patterns = (file.refactorings.head as SchemeInstanceRule).matchingPattern.patterns + assertTrue(patterns.forall[it instanceof PMetaVariable]) + + assertEquals((patterns.get(0) as PMetaVariable).type, MetaVariableType.CODE) + assertEquals((patterns.get(1) as PMetaVariable).type, MetaVariableType.NAME) + assertEquals((patterns.get(2) as PMetaVariable).type, MetaVariableType.TYPE) + assertEquals((patterns.get(3) as PMetaVariable).type, MetaVariableType.VISIBILITY) + assertEquals((patterns.get(4) as PMetaVariable).type, MetaVariableType.ARGUMENT) + assertEquals((patterns.get(5) as PMetaVariable).type, MetaVariableType.PARAMETER) + } +} diff --git a/hu.elte.refjava.lang.ui/.classpath b/hu.elte.refjava.lang.ui/.classpath index 8018385..bc001b4 100644 --- a/hu.elte.refjava.lang.ui/.classpath +++ b/hu.elte.refjava.lang.ui/.classpath @@ -1,9 +1,9 @@ + + - - diff --git a/hu.elte.refjava.lang.ui/.settings/org.eclipse.jdt.core.prefs b/hu.elte.refjava.lang.ui/.settings/org.eclipse.jdt.core.prefs index dd5a2bb..3efb014 100644 --- a/hu.elte.refjava.lang.ui/.settings/org.eclipse.jdt.core.prefs +++ b/hu.elte.refjava.lang.ui/.settings/org.eclipse.jdt.core.prefs @@ -9,8 +9,8 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul org.eclipse.jdt.core.compiler.annotation.nullable.secondary= org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=10 -org.eclipse.jdt.core.compiler.compliance=10 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.problem.APILeak=warning org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning org.eclipse.jdt.core.compiler.problem.assertIdentifier=error @@ -22,6 +22,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled org.eclipse.jdt.core.compiler.problem.discouragedReference=ignore org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore @@ -67,6 +68,7 @@ org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=igno org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled @@ -105,4 +107,4 @@ org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=10 +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/hu.elte.refjava.lang.ui/META-INF/MANIFEST.MF b/hu.elte.refjava.lang.ui/META-INF/MANIFEST.MF index 41c2685..e054ca4 100644 --- a/hu.elte.refjava.lang.ui/META-INF/MANIFEST.MF +++ b/hu.elte.refjava.lang.ui/META-INF/MANIFEST.MF @@ -19,12 +19,16 @@ Require-Bundle: hu.elte.refjava, org.eclipse.xtext.xbase.ui, org.eclipse.compare, org.eclipse.xtext.builder, - org.eclipse.xtend.lib;resolution:=optional, + org.eclipse.xtend.lib;bundle-version="2.14.0";resolution:=optional, org.eclipse.jdt.debug.ui Import-Package: org.apache.log4j -Bundle-RequiredExecutionEnvironment: JavaSE-10 -Export-Package: hu.elte.refjava.lang.ui.internal, - hu.elte.refjava.lang.ui.quickfix, +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Export-Package: hu.elte.refjava.lang.ui, + hu.elte.refjava.lang.ui.contentassist, hu.elte.refjava.lang.ui.editor, - hu.elte.refjava.lang.ui.contentassist + hu.elte.refjava.lang.ui.internal, + hu.elte.refjava.lang.ui.labeling, + hu.elte.refjava.lang.ui.outline, + hu.elte.refjava.lang.ui.quickfix, + hu.elte.refjava.lang.validation Bundle-Activator: hu.elte.refjava.lang.ui.internal.LangActivator diff --git a/hu.elte.refjava.lang.ui/plugin.xml b/hu.elte.refjava.lang.ui/plugin.xml index 5290adf..f9552e7 100644 --- a/hu.elte.refjava.lang.ui/plugin.xml +++ b/hu.elte.refjava.lang.ui/plugin.xml @@ -475,42 +475,6 @@ - - - - - - - - - - - - - - - - + diff --git a/hu.elte.refjava.lang/.launch/Generate RefJava (refjava) Language Infrastructure.launch b/hu.elte.refjava.lang/.launch/Generate RefJava (refjava) Language Infrastructure.launch index 6b8b3eb..77ba186 100644 --- a/hu.elte.refjava.lang/.launch/Generate RefJava (refjava) Language Infrastructure.launch +++ b/hu.elte.refjava.lang/.launch/Generate RefJava (refjava) Language Infrastructure.launch @@ -1,18 +1,19 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/hu.elte.refjava.lang/.launch/Launch Runtime Eclipse.launch b/hu.elte.refjava.lang/.launch/Launch Runtime Eclipse.launch index f53c414..ec3e469 100644 --- a/hu.elte.refjava.lang/.launch/Launch Runtime Eclipse.launch +++ b/hu.elte.refjava.lang/.launch/Launch Runtime Eclipse.launch @@ -1,35 +1,35 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hu.elte.refjava.lang/.settings/org.eclipse.jdt.core.prefs b/hu.elte.refjava.lang/.settings/org.eclipse.jdt.core.prefs index ca12074..f1bf70b 100644 --- a/hu.elte.refjava.lang/.settings/org.eclipse.jdt.core.prefs +++ b/hu.elte.refjava.lang/.settings/org.eclipse.jdt.core.prefs @@ -10,9 +10,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable.secondary= org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=10 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=10 +org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -27,6 +27,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled org.eclipse.jdt.core.compiler.problem.discouragedReference=ignore org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore @@ -72,6 +73,7 @@ org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=igno org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled @@ -110,4 +112,4 @@ org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=10 +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/hu.elte.refjava.lang/.settings/org.eclipse.ltk.core.refactoring.prefs b/hu.elte.refjava.lang/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 0000000..b196c64 --- /dev/null +++ b/hu.elte.refjava.lang/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/hu.elte.refjava.lang/META-INF/MANIFEST.MF b/hu.elte.refjava.lang/META-INF/MANIFEST.MF index 8f5a5c7..9dd744a 100644 --- a/hu.elte.refjava.lang/META-INF/MANIFEST.MF +++ b/hu.elte.refjava.lang/META-INF/MANIFEST.MF @@ -12,17 +12,23 @@ Require-Bundle: org.eclipse.xtext, org.eclipse.emf.ecore, org.eclipse.xtext.common.types, org.eclipse.xtext.xbase.lib;bundle-version="2.14.0", - org.objectweb.asm;bundle-version="[6.2.1,6.3.0)";resolution:=optional, + org.objectweb.asm;bundle-version="[7.1.0,7.2.0)";resolution:=optional, org.eclipse.xtext.util, org.eclipse.emf.common, org.eclipse.xtend.lib;bundle-version="2.14.0", org.antlr.runtime;bundle-version="[3.2.0,3.2.1)", org.eclipse.jdt.core;bundle-version="3.15.0", org.eclipse.jface.text;bundle-version="3.14.0", - org.eclipse.xtext.testing;bundle-version="2.15.0" -Bundle-RequiredExecutionEnvironment: JavaSE-10 + org.eclipse.xtext.testing;bundle-version="2.15.0", + com.google.guava, + org.eclipse.xtend.lib.macro, + org.junit, + org.eclipse.emf.ecore.xcore;bundle-version="1.11.0" +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Export-Package: hu.elte.refjava.api, + hu.elte.refjava.api.patterns, hu.elte.refjava.lang, + hu.elte.refjava.lang.compiler, hu.elte.refjava.lang.jvmmodel, hu.elte.refjava.lang.parser.antlr, hu.elte.refjava.lang.parser.antlr.internal, @@ -32,5 +38,7 @@ Export-Package: hu.elte.refjava.api, hu.elte.refjava.lang.scoping, hu.elte.refjava.lang.serializer, hu.elte.refjava.lang.services, - hu.elte.refjava.lang.validation + hu.elte.refjava.lang.typesystem, + hu.elte.refjava.lang.validation, + hu.elte.refjava.tests Import-Package: org.apache.log4j diff --git a/hu.elte.refjava.lang/plugin.xml_gen b/hu.elte.refjava.lang/plugin.xml_gen new file mode 100644 index 0000000..4de08d7 --- /dev/null +++ b/hu.elte.refjava.lang/plugin.xml_gen @@ -0,0 +1,10 @@ + + + + + + + diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/api/BlockRefactoring.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/api/BlockRefactoring.xtend new file mode 100644 index 0000000..8b001b4 --- /dev/null +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/api/BlockRefactoring.xtend @@ -0,0 +1,161 @@ +package hu.elte.refjava.api + +import hu.elte.refjava.api.patterns.ASTBuilder +import hu.elte.refjava.api.patterns.PatternMatcher +import hu.elte.refjava.api.patterns.PatternParser +import hu.elte.refjava.api.patterns.Utils +import java.util.List +import org.eclipse.jdt.core.dom.ASTNode +import org.eclipse.jdt.core.dom.SimpleName +import org.eclipse.jdt.core.dom.TypeDeclaration +import org.eclipse.jface.text.IDocument + +class BlockRefactoring implements Refactoring { + + List target + IDocument document + + val PatternMatcher matcher + val ASTBuilder builder + protected String matchingTypeReferenceString + protected String replacementTypeReferenceString + protected String targetTypeReferenceString + protected String definitionTypeReferenceString + List replacement + + protected new(String matchingPatternString, String replacementPatternString) { + nameBindings.clear + typeBindings.clear + parameterBindings.clear + visibilityBindings.clear + argumentBindings.clear + setMetaVariables + matcher = new PatternMatcher(PatternParser.parse(matchingPatternString)) + builder = new ASTBuilder(PatternParser.parse(replacementPatternString)) + } + + override init(List target, IDocument document, List allTypeDeclInWorkspace) { + this.target = target + this.document = document + Check.allTypeDeclarationInWorkSpace = allTypeDeclInWorkspace + } + + def setTarget(List newTarget) { + this.target = newTarget + } + + override apply() { + return if(!safeTargetCheck) { + Status.TARGET_MATCH_FAILED + } else if (!safeMatch) { + Status.MATCH_FAILED + } else if (!safeCheck) { + Status.CHECK_FAILED + } else if (!safeBuild) { + Status.BUILD_FAILED + } else if (!safeReplace) { + Status.REPLACEMENT_FAILED + } else { + Status.SUCCESS + } + } + + def private safeMatch() { + try { + if (!matcher.match(target, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, matchingTypeReferenceString) ) { + return false + } + target = matcher.modifiedTarget.toList + bindings.putAll(matcher.bindings) + true + } catch (Exception e) { + println(e) + false + } + } + + def protected void setMetaVariables() { + //empty + } + + def protected safeTargetCheck() { + true + } + + def protected targetCheck(String targetPatternString) { + try { + if(!matcher.match(PatternParser.parse(targetPatternString), target, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, targetTypeReferenceString)) { + return false + } + bindings.putAll(matcher.bindings) + true + } catch (Exception e) { + println(e) + false + } + } + + def private safeCheck() { + try { + check() + } catch (Exception e) { + println(e) + false + } + } + + def protected check() { + Check.isInsideBlock(target) + } + + def private safeBuild() { + try { + replacement = builder.build(target.head.AST, bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, replacementTypeReferenceString) + true + } catch (Exception e) { + println(e) + false + } + } + + def private safeReplace() { + try { + + val targetTypeDecl = Utils.getTypeDeclaration(target.head) + val assignmentBeforeReplacement = Check.getAssignmentsInClass(targetTypeDecl) + + val rewrite = builder.rewrite + target.tail.forEach[rewrite.remove(it, null)] + val group = rewrite.createGroupNode(replacement) + rewrite.replace(target.head, group, null) + val edits = rewrite.rewriteAST(document, null) + edits.apply(document) + + val iCompUnit = Utils.getICompilationUnit(target.head) + val compUnit = Utils.parseSourceCode(iCompUnit) + val assignmentsAfterReplacement = Check.getAssignmentsInClass(compUnit.types.findFirst[ + (it as TypeDeclaration).resolveBinding.qualifiedName == targetTypeDecl.resolveBinding.qualifiedName] as TypeDeclaration) + + compUnit.recordModifications + val it1 = assignmentBeforeReplacement.iterator + val it2 = assignmentsAfterReplacement.iterator + while (it1.hasNext) { + val value1 = it1.next + val value2 = it2.next + if(Check.referredField(value1.leftHandSide as SimpleName) !== null && Check.referredField(value2.leftHandSide as SimpleName) === null) { + val thisExpression = compUnit.AST.newThisExpression + val fieldAccess = compUnit.AST.newFieldAccess + fieldAccess.name.identifier = (value2.leftHandSide as SimpleName).identifier + fieldAccess.expression = thisExpression + value2.leftHandSide = fieldAccess + } + } + + Utils.applyChanges(compUnit, document) + true + } catch (Exception e) { + println(e) + false + } + } +} \ No newline at end of file diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/api/Check.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/api/Check.xtend index 32e2f9f..169e639 100644 --- a/hu.elte.refjava.lang/src/hu/elte/refjava/api/Check.xtend +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/api/Check.xtend @@ -1,34 +1,98 @@ package hu.elte.refjava.api +import hu.elte.refjava.api.patterns.Utils +import hu.elte.refjava.lang.refJava.Visibility +import java.lang.reflect.Modifier import java.util.List +import java.util.Queue import org.eclipse.jdt.core.dom.ASTNode import org.eclipse.jdt.core.dom.ASTVisitor +import org.eclipse.jdt.core.dom.AnonymousClassDeclaration +import org.eclipse.jdt.core.dom.ArrayType +import org.eclipse.jdt.core.dom.Assignment import org.eclipse.jdt.core.dom.Block +import org.eclipse.jdt.core.dom.ClassInstanceCreation +import org.eclipse.jdt.core.dom.ExpressionStatement +import org.eclipse.jdt.core.dom.FieldDeclaration +import org.eclipse.jdt.core.dom.ITypeBinding +import org.eclipse.jdt.core.dom.MethodDeclaration +import org.eclipse.jdt.core.dom.MethodInvocation +import org.eclipse.jdt.core.dom.Name +import org.eclipse.jdt.core.dom.QualifiedName +import org.eclipse.jdt.core.dom.ReturnStatement import org.eclipse.jdt.core.dom.SimpleName +import org.eclipse.jdt.core.dom.SingleVariableDeclaration import org.eclipse.jdt.core.dom.Statement +import org.eclipse.jdt.core.dom.Type +import org.eclipse.jdt.core.dom.TypeDeclaration import org.eclipse.jdt.core.dom.VariableDeclarationFragment import org.eclipse.jdt.core.dom.VariableDeclarationStatement class Check { + protected static List allTypeDeclarationInWorkSpace + + ///////////////// + //public checks// + ///////////////// + + /** + * Determines whether the given list of ASTNodes consist of one element. + * @param target the list of ASTNodes + * @return true, if target consist of on element, false otherwise + */ + def static isSingle(List target) { + target.size == 1 + } + + /** + * Determines whether the given ASTNode is located inside a Block. + * @param node the ASTnode + * @return true, if node located inside a Block, false otherwise + */ def static isInsideBlock(ASTNode node) { node.parent instanceof Block } - + + /** + * Determines whether the all of the given list of ASTNodes' element is located in a Block. + * @param nodes the list of ASTNodes + * @return true, if all of node's element located inside a Block, false otherwise + */ def static isInsideBlock(List nodes) { nodes.forall[isInsideBlock] } - def static isVariableDeclaration(ASTNode node) { + /** + * Determines whether the given ASTNode is a variable declaration. + * @param node the list of ASTNodes + * @return true, if node is a variable declaration, false otherwise + */ + def dispatch static isVariableDeclaration(ASTNode node) { node instanceof VariableDeclarationStatement } + + /** + * Determines whether the all of the given list of ASTNodes' element is a variable declaration. + * @param node the list of ASTNodes + * @return true, if all of nodes' element is a variable declaration, false otherwise + */ + def dispatch static isVariableDeclaration(List nodes) { + nodes.forall[it instanceof VariableDeclarationStatement] + } - def static asVariableDeclaration(ASTNode node) { + def dispatch static asVariableDeclaration(ASTNode node) { if (node instanceof VariableDeclarationStatement) { node } } + def dispatch static asVariableDeclaration(List nodes) { + if (nodes.isVariableDeclaration) { + nodes as List + } + } + def static blockRemainder(ASTNode node) { val parent = node.parent if (parent instanceof Block) { @@ -36,7 +100,7 @@ class Check { } } - def static isReferencedIn(VariableDeclarationStatement varDecl, List nodes) { + def dispatch static isReferencedIn(VariableDeclarationStatement varDecl, List nodes) { (varDecl.fragments as List).exists [ val varBinding = resolveBinding nodes.exists [ @@ -48,15 +112,1081 @@ class Check { found = true return false } - return true } } - accept(visitor) return visitor.found ] ] } + + def dispatch static boolean isReferencedIn(List varDeclList, List nodes) { + varDeclList.exists[it.isReferencedIn(nodes)] + } + + + //OPTIONAL CHECK + //determines whether the selected nodes contains a ReturnExpression with an expression that is not null + def static containsValueReturn(List target) { + target.exists [ + val visitor = new ASTVisitor() { + public var found = false + override visit(ReturnStatement statement) { + if (statement.expression !== null) { + found = true + return false + } + true + } + } + it.accept(visitor) + visitor.found + ] + } + + + //OPTIONAL CHECK + //determines whether the selected nodes contains a ReturnExpression with an expression that is null + def static containsVoidReturn(List target) { + target.exists [ + val visitor = new ASTVisitor() { + public var found = false + override visit(ReturnStatement statement) { + if (statement.expression === null) { + found = true + return false + } + true + } + } + it.accept(visitor) + visitor.found + ] + } + + //OPTIONAL CHECK + //gets the first ReturnExpression which expression is null from the selected nodes.. returns null if such a node doesn't exists + def static getVoidReturn(List target) { + val List result = newArrayList + target.exists [ + val visitor = new ASTVisitor() { + public var found = false + override visit(ReturnStatement statement) { + if (statement.expression === null) { + found = true + result.add(statement) + return false + } + true + } + } + it.accept(visitor) + visitor.found + ] + result.head as ReturnStatement + } + + /** + * Determines whether if the a ReturnStatement is the last possible execution node of the Block which has the statement. + * @param statement the ReturnStatement + * @return true, if its the last execution path, false otherwise + */ + def static isLastExecutionNode(ReturnStatement statement) { + var firstLevelNodeInMethod = statement as ASTNode + while (!((firstLevelNodeInMethod.parent instanceof Block) && ((firstLevelNodeInMethod.parent as Block).parent instanceof MethodDeclaration))) { + firstLevelNodeInMethod = firstLevelNodeInMethod.parent + } + val node = firstLevelNodeInMethod + val nodesAfterReturnStatement = (firstLevelNodeInMethod.parent as Block).statements.dropWhile[it != node] + nodesAfterReturnStatement.size == 1 && nodesAfterReturnStatement.head == node + } + + /** + * Gets the identifier of the first element of the given list of ASTNodes. + * Note: Only works if the first element of the given list is a MethodDeclaration. Returns null otherwise. + * @param target the list of ASTNodes + * @return the identifier of target's first element + */ + def static String getMethodName(List target) { + if (target.head instanceof MethodDeclaration) { + return (target.head as MethodDeclaration).name.identifier + } + null + } + + /** + * Gets the identifiers of the first element of the given list of ASTNodes. + * Note: Only works if the first element of the given list is a FieldDeclaration. Returns null otherwise. + * @param target the list of ASTNodes + * @return the list of identifiers of target's first element + */ + def static getFragmentNames(List target) { + if(target.head instanceof FieldDeclaration) { + val fragments = (target.head as FieldDeclaration).fragments as List + var List fragmentNames = newArrayList + for(fragment : fragments) { + fragmentNames.add(fragment.name.identifier) + } + return fragmentNames + } + null + } + + /** + * Gets the type of a list of ASTNodes' first element. + * Note: Only works if the first element of the given list is either a FieldDeclaration, or a MethodDeclaration. Returns null otherwise. + * @param target the list of ASTNodes + * @return the type or return type of target's first element (org.eclipse.jdt.core.dom.Type) + */ + def dispatch static Type type(List target) { + if (target.head instanceof MethodDeclaration) { + return type(target.head) + } else if (target.head instanceof FieldDeclaration) { + return type(target.head) + } + null + } + + /** + * Gets the visibility of a list of ASTNodes' first element. + * Note: Only works if the first element of the given list is either a FieldDeclaration, or a MethodDeclaration. Returns null otherwise. + * @param target the list of ASTNodes + * @return the visibility of target's first element (hu.elte.refjava.lang.refJava.Visibility) + */ + def dispatch static Visibility visibility(List target) { + if(target.head instanceof MethodDeclaration) { + return visibility(target.head as MethodDeclaration) + } else if (target.head instanceof FieldDeclaration) { + return visibility(target.head as FieldDeclaration) + } + null + } + + /** + * Gets the parameters of a list of ASTNodes' first element. + * Note: Only works if the first element of the given list is a MethodDeclaration. Returns null otherwise. + * @param target the list of ASTNodes + * @return the parameters of target's first element (list of org.eclipse.jdt.core.dom.SingleVariableDeclaration) + */ + def dispatch static parameters(List target) { + if(target.head instanceof MethodDeclaration) { + return (target.head as MethodDeclaration).parameters as List + } + null + } + + /** + * Gets the class of a list of ASTNode's first element. + * Note: The the returned class will be on the same AST as the given list of ASTNodes' first element. + * @param target the list of ASTNodes + * @return the class of target's first element (org.eclipse.jdt.core.dom.TypeDeclaration) + */ + def static enclosingClass(List target) { + Utils.getTypeDeclaration(target.head) + } + + /** + * Generates a fresh TypeDeclaration identifier in the workspace. + * @return the newly generated identifier + */ + def static generateNewName() { + var int i = 1 + var newName = "newLambda" + while(!isFresh(newName)) { + newName = "newLambda" + i++ + } + newName + } + + /** + * Determines if an identifier is used in the workspace as a TypeDeclaration identifier. + * @param name the identifier + * @return true, if identifier isn't used, false otherwise + */ + def static isFresh(String name) { + !allTypeDeclarationInWorkSpace.exists[it.name.identifier == name] + } + + /** + * Gets all references to a MethodDeclartion that can get accessed via public interface. + * @param methodName the MethodDeclaration's name + * @param methodParameters the MethodDeclaration's parameters + * @param targetTypeDeclaration the MethodDeclaration's TypeDeclaration + * @return list of ASTNodes + */ + def protected static publicReferences(String methodName, List methodParameters, TypeDeclaration targetTypeDeclaration) { + references("public", methodName, methodParameters, targetTypeDeclaration) + } + + /** + * Gets all references to a FieldDeclaration that can get accessed via public interface. + * @param fragmentNames the FieldDeclaration's fragment names + * @param targetTypeDeclaration the FieldDeclaration's TypeDeclaration + * @return list of ASTNodes + */ + def protected static publicReferences(List fragmentNames, TypeDeclaration targetTypeDecl) { + references("public", fragmentNames, targetTypeDecl) + } + + //////////////////////////// + //private/protected checks// + //////////////////////////// + + /** + * Gets all Assignments from a TypeDeclareation. + * @param typeDecl the target TypeDeclaration + * @return all Assignments in the target TypeDeclaration + */ + def protected static getAssignmentsInClass(TypeDeclaration typeDecl) { + val List assignments = newArrayList + val visitor = new ASTVisitor() { + override visit(Assignment assignment) { + if (assignment.leftHandSide instanceof SimpleName) { + assignments.add(assignment) + } + return true + } + } + typeDecl.accept(visitor) + assignments + } + + //gets all references to a TypeDeclaration in the workspace, except the TypeDeclaration itself + //TODO + def protected static references(TypeDeclaration typeDecl) { + val List references = newArrayList + val binding = typeDecl.name.resolveBinding + allTypeDeclarationInWorkSpace.forEach[ + val visitor = new ASTVisitor() { + override visit(SimpleName name) { + if (name.resolveBinding.isEqualTo(binding) && name != typeDecl.name) { + references.add(name) + } + true + } + } + it.accept(visitor) + ] + references + } + + /** + * //TODO + * @param references + * @param target + * @return + */ + def protected static contains(List references, List target) { + if(target.head instanceof ExpressionStatement && + (target.head as ExpressionStatement).expression instanceof MethodInvocation && + ((target.head as ExpressionStatement).expression as MethodInvocation).expression instanceof ClassInstanceCreation && + (((target.head as ExpressionStatement).expression as MethodInvocation).expression as ClassInstanceCreation).anonymousClassDeclaration !== null ) { + + for(refs : references) { + if( (refs as SimpleName).resolveBinding.isEqualTo( (((target.head as ExpressionStatement).expression as MethodInvocation).expression as ClassInstanceCreation).type.resolveBinding) ) { + return true + } + } + } + false + } + + /** + * Gets the type identifier of a ClassInstanceCreation (lambda expression's expression). + * @param exprStatement the lambda expression (as an ExpressionStatement, which has a MethodInvocation expression) + * @return the lambda expression's identifier + */ + def protected static getLambdaName(ExpressionStatement exprStatement) { + ((exprStatement.expression as MethodInvocation).expression as ClassInstanceCreation).type.toString + } + + /** + * Gets a ClassInstanceCreation's anonymous class declaration. + * @param exprStatement the lambda expression (as an ExpressionStatement, which has a MethodInvocation expression) + * @return the lambda expression's body + */ + def protected static getLambdaBody(ExpressionStatement exprStatement) { + ((exprStatement.expression as MethodInvocation).expression as ClassInstanceCreation).anonymousClassDeclaration + } + + /** + * Gets all Assignments from an anonymous class declaration. + * @param anonClass the AnonymousClassDeclaration + * @return all Assignments from the given AnonymousClassDeclaration + */ + def protected static lambdaVariableAssignments(AnonymousClassDeclaration anonClass) { + val List variableWrites = newArrayList + anonClass.bodyDeclarations.forEach [ + val visitor = new ASTVisitor() { + override visit(Assignment assignment) { + variableWrites.add(assignment) + } + } + (it as ASTNode).accept(visitor) + ] + variableWrites + } + + /** + * Determines whether an Assignment's left hand side is declared in the given AnonymousClassDeclaration + * @param assignment the Assignment + * @param anonClass the AnonymousClassDeclaration + * @return true, if assignment's left hand side declared in anonClass + */ + def protected static isDeclaredIn(Assignment assignment, AnonymousClassDeclaration anonClass) { + if(assignment.leftHandSide instanceof SimpleName) { + val varName = (assignment.leftHandSide as SimpleName) + val List namesList = newArrayList + anonClass.bodyDeclarations.forEach[ + val visitor = new ASTVisitor() { + override visit(SimpleName name) { + if(name.identifier == varName.identifier) { + namesList.add(name) + return true + } + true + } + } + (it as ASTNode).accept(visitor) + ] + for (name : namesList) { + if (name == varName) { + return false + } else if (name.parent instanceof VariableDeclarationFragment) { + return true + } + } + return false + } + true + } + + /** + * Gets the return type of a MethodDeclaration. + * @param methodDecl the MethodDeclaration + * @return the return type of the given MethodDeclaration (org.eclipse.jdt.core.dom.Type) + */ + def protected dispatch static type(MethodDeclaration methodDecl) { + methodDecl.returnType2 + } + + /** + * Gets the type of a FieldDeclaration. + * @param fieldDecl the FieldDeclaration + * @return the type of the given FieldDeclaration (org.eclipse.jdt.core.dom.Type) + */ + def protected dispatch static type(FieldDeclaration fieldDecl) { + fieldDecl.type + } + + /** + * Gets the visibility of a FieldDeclaration. + * @param fieldDecl the FieldDeclaration + * @return the visibility of the given FieldDeclaration (hu.elte.refjava.lang.refJava.Visibility) + */ + def protected dispatch static visibility(FieldDeclaration fieldDecl) { + val modifiers = fieldDecl.getModifiers + switch modifiers { + case modifiers.bitwiseAnd(Modifier.PUBLIC) > 0 : Visibility.PUBLIC + case modifiers.bitwiseAnd(Modifier.PRIVATE) > 0 : Visibility.PRIVATE + case modifiers.bitwiseAnd(Modifier.PROTECTED) > 0 : Visibility.PROTECTED + case modifiers.bitwiseAnd(Modifier.PROTECTED) == 0 && modifiers.bitwiseAnd(Modifier.PRIVATE) == 0 && modifiers.bitwiseAnd(Modifier.PUBLIC) == 0 : Visibility.PACKAGE + } + } + + /** + * Gets the visibility of a MethodDeclaration. + * @param methodDecl the MethodDeclaration + * @return the visibility of the given MethodDeclaration (hu.elte.refjava.lang.refJava.Visibility) + */ + def protected dispatch static visibility(MethodDeclaration methodDecl) { + val modifiers = methodDecl.getModifiers + switch modifiers { + case modifiers.bitwiseAnd(Modifier.PUBLIC) > 0 : Visibility.PUBLIC + case modifiers.bitwiseAnd(Modifier.PRIVATE) > 0 : Visibility.PRIVATE + case modifiers.bitwiseAnd(Modifier.PROTECTED) > 0 : Visibility.PROTECTED + case modifiers.bitwiseAnd(Modifier.PROTECTED) == 0 && modifiers.bitwiseAnd(Modifier.PRIVATE) == 0 && modifiers.bitwiseAnd(Modifier.PUBLIC) == 0 : Visibility.PACKAGE + } + } + + /** + * Gets the parameters of a MethodDeclaration. + * @param methodDecl the MethodDeclaration + * @return the parameters of the given MethodDeclaration (list of org.eclipse.jdt.core.dom.SingleVariableDeclaration) + */ + def protected dispatch static parameters(MethodDeclaration methodDecl) { + methodDecl.parameters as List + } + + /** + * Gets the superclass of a TypeDeclaration. + * Note: Only works if the given TypeDeclaration has a superclass. Also, the returned superclass will be on an another AST. + * @param typeDecl the TypeDeclaration + * @return typeDecl's superclass (org.eclipse.jdt.core.dom.TypeDeclaration) + */ + def protected static superClass(TypeDeclaration typeDecl) { + allTypeDeclarationInWorkSpace.findFirst[it.resolveBinding.qualifiedName == typeDecl.superclassType.resolveBinding.qualifiedName] + } + + + /** + * Determines whether a TypeDeclaration has a superclass. + * @param typeDecl the TypeDeclaration + * @return true, if typeDecl has a superclass, false otherwise + */ + def protected static hasSuperClass(TypeDeclaration typeDecl) { + return typeDecl.superclassType !== null + } + + /** + * Determines whether a list of ASTNode's first element's visibility is 'private'. + * Note: Only works if the given list of ASTNodes' first element if either a FieldDeclaration, or a MethodDeclaration. Returns false otherwise. + * @param target the list of ASTNodes + * @return true, if target's first element's visibility if 'private', false otherwise + */ + def protected static isPrivate(List target) { + if (target.head instanceof FieldDeclaration) { + return Modifier.isPrivate( (target.head as FieldDeclaration).getModifiers()) + } else if(target.head instanceof MethodDeclaration) { + return Modifier.isPrivate( (target.head as MethodDeclaration).getModifiers()) + } + false + } + + /** + * Gets all the references from a TypeDeclarations to the first element given list of ASTNodes. + * Note: Only works if the first element of the given list is either a FieldDeclaration, or a MethodDeclaration. Returns an empty list otherwise. + * @param target the list of ASTNodes + * @param typeDecl the TypeDeclaration + * @return the references to the first element of target + */ + def protected static references(List target, TypeDeclaration typeDecl) { + val bodyDeclarations = typeDecl.bodyDeclarations + val List refs = newArrayList + + if (target.head instanceof FieldDeclaration) { + val fieldDecl = target.head as FieldDeclaration + val fragments = fieldDecl.fragments as List + for(fragment : fragments) { + val binding = fragment.resolveBinding + for(declaration : bodyDeclarations) { + val visitor = new ASTVisitor() { + override visit(SimpleName name) { + if (name.resolveBinding.isEqualTo(binding) && name != fragment.name) { + refs.add(name) + } + return true + } + } + (declaration as ASTNode).accept(visitor) + } + } + } else if (target.head instanceof MethodDeclaration) { + val methodDecl = target.head as MethodDeclaration + val binding = methodDecl.resolveBinding + for(declaration : bodyDeclarations) { + val visitor = new ASTVisitor() { + override visit(SimpleName name) { + if (name.resolveBinding.isEqualTo(binding) && name != methodDecl.name) { + refs.add(name) + } + return true + } + } + (declaration as ASTNode).accept(visitor) + } + } + refs + } + + /** + * Determines whether a given identifier is used as a FieldDeclaration's identifier in a TypeDeclaration. + * @param fragmentName the identifier + * @param typeDecl the TypeDeclaration + * @return true, if fragmentName isn't used as an identifier , false otherwise + */ + def private static isUniqueFieldIn(String fragmentName, TypeDeclaration typeDecl) { + typeDecl.bodyDeclarations.filter[it instanceof FieldDeclaration].forall[ + !((it as FieldDeclaration).fragments as List).exists[ + it.name.identifier == fragmentName + ] + ] + } + + /** + * Determines whether neither of the given identifiers is used as a FieldDeclaration's identifier in a TypeDeclaration. + * @param fragmentNames the list of identifiers + * @param typeDecl the TypeDeclaration + * @return true, if none of fragmentNames used as an identifier , false otherwise + */ + def protected static isUniqueFieldIn(List fragmentNames, TypeDeclaration typeDecl) { + fragmentNames.forall[ + isUniqueFieldIn(it, typeDecl) + ] + } + + /** + * Determines whether if a method exists with the same name and parameter types as the given name and parameter types in a TypeDeclaration. + * @param methodName the method's name + * @param parameters the method's parameters + * @param typeDecl the TypeDeclaration + * @return true, if there isn't a method with the same name and parameter, false otherwise + */ + def protected static isUniqueMethodIn(String methodName, List parameters, TypeDeclaration typeDecl) { + val methodsInClass = typeDecl.bodyDeclarations.filter[it instanceof MethodDeclaration] + + for (method : methodsInClass) { + if ((method as MethodDeclaration).name.identifier == methodName && parameters.size == ((method as MethodDeclaration).parameters.size)) { + if (parameters.size == 0) { + return false + } + + val it1 = parameters.iterator + val it2 = ((method as MethodDeclaration).parameters as List).iterator + var boolean l = true + while(it1.hasNext && l) { + l = it1.next.type.toString == it2.next.type.toString + } + + if (l) { + return false + } + } + } + true + } + + /** + * Gets all the FielDeclaration that have a reference inside the first element of the given list of ASTNodes' body, in a TypeDeclaration. + * Note: Only works if the fist element of the given list is a MethodDeclaration. Returns an empty list otherwise. + * @param target the list of ASTNodes + * @param typeDecl the TypeDeclaration + * @return the referenced FieldDeclarations + */ + def protected static accessedFieldsOfEnclosingClass(List target, TypeDeclaration typeDecl) { + val methodDecl = target.head as MethodDeclaration + + val List accessedFields = newArrayList + val methodBody = methodDecl.body + val classFields = typeDecl.bodyDeclarations.filter[it instanceof FieldDeclaration] + for (field : classFields) { + val fragments = (field as FieldDeclaration).fragments as List + for(fragment : fragments) { + val binding = fragment.resolveBinding + methodBody.statements.exists[ + val visitor = new ASTVisitor() { + public var found = false + + override visit(SimpleName name) { + if(name.resolveBinding.isEqualTo(binding)) { + found = true + return false + } + return true + } + } + (it as ASTNode).accept(visitor) + if(visitor.found) { + accessedFields.add(field as ASTNode) + } + return visitor.found + ] + } + } + accessedFields + } + + /** + * Gets all the MethodDeclarations that have a reference inside the first element of the given list of ASTNodes' body, in a TypeDeclaration. + * Note: Only works if the fist element of the given list is a MethodDeclaration. Returns an empty list otherwise. + * @param target the list of ASTNodes + * @param typeDecl the TypeDeclaration + * @return the referenced MethodDeclarations + */ + def protected static accessedMethodsOfEnclosingClass(List target, TypeDeclaration typeDecl) { + val methodDecl = target.head as MethodDeclaration + + val List accessedMethods = newArrayList + val methodBody = methodDecl.body + val classMethods = typeDecl.bodyDeclarations.filter[it instanceof MethodDeclaration] + for (method : classMethods) { + val binding = (method as MethodDeclaration).resolveBinding + methodBody.statements.exists[ + val visitor = new ASTVisitor() { + public var found = false + + override visit(SimpleName name) { + if(name.resolveBinding.isEqualTo(binding) && name != methodDecl.name) { + found = true + return false + } + return true + } + } + (it as ASTNode).accept(visitor) + if(visitor.found) { + accessedMethods.add(method as ASTNode) + } + return visitor.found + ] + } + accessedMethods + } + + /** + * Determines whether if a method exists with the same name and parameter types as the given name and parameter types in a one of the given TypeDeclaration's superclass. + * @param methodName the method's name + * @param parameters the method's parameters + * @param typeDecl the TypeDeclaration + * @return true, if there isn't a method with the same name and parameter, false otherwise + */ + def protected static isOverrideIn(String methodName, List parameters, TypeDeclaration typeDecl){ + var superClassToCheck = typeDecl + var found = false + + while (superClassToCheck.hasSuperClass && !found) { + superClassToCheck = superClassToCheck.superClass + found = !isUniqueMethodIn(methodName, parameters, superClassToCheck) && !Modifier.isPrivate(getMethodFromClass(methodName, parameters, superClassToCheck).getModifiers) + } + found + } + + /** + * Gets the method with the same name and parameter types as the given name and parameter types from a first of the given TypeDeclaration's superclass, that has one. + * Note: Only works if such a method exists. + * @param methodName the method's name + * @param parameters the method's parameters + * @param typeDecl the TypeDeclaration + * @return the method with the same name and parameter types + */ + def protected static overridenMethodFrom(String methodName, List parameters, TypeDeclaration typeDecl) { + var superClassToCheck = typeDecl + var found = false + var MethodDeclaration method + while (superClassToCheck.hasSuperClass && !found) { + superClassToCheck = superClassToCheck.superClass + if(!isUniqueMethodIn(methodName, parameters, superClassToCheck)) { + found = true + method = getMethodFromClass(methodName, parameters, superClassToCheck) + } + } + method + } + + /** + * Gets the FieldDeclaration from a TypeDeclaration with the given list of identifiers. + * @param fragmentNames the list of identifiers + * @param typeDecl the TypeDeclaration + * @return the FieldDeclaration with the given list of identifiers + */ + def protected static getFieldFromClass(List fragmentNames, TypeDeclaration typeDecl) { + typeDecl.bodyDeclarations.findFirst[ + val iter = fragmentNames.iterator + it instanceof FieldDeclaration && ((it as FieldDeclaration).fragments as List).forall[ + if (iter.hasNext){ + it.name.identifier == iter.next + } else { + false + } + ] + ] as FieldDeclaration + } + + /** + * Gets the MethodDeclaration from a TypeDeclaration with the given name and parameter types. + * @param methodName the visibility to be examined + * @param parameters the visibility that targetVisibility will be compared to + * @param typeDecl the TypeDeclaration + * @return the MethodDeclaration with the given name and parameter types + */ + def protected static getMethodFromClass(String methodName, List parameters, TypeDeclaration typeDecl) { + val methodsInClass = typeDecl.bodyDeclarations.filter[it instanceof MethodDeclaration] + var MethodDeclaration result + for(method : methodsInClass) { + if ((method as MethodDeclaration).name.identifier == methodName && parameters.size == ((method as MethodDeclaration).parameters.size)) { + if(parameters.size == 0) { + result = method as MethodDeclaration + } + + val it1 = parameters.iterator + val it2 = ((method as MethodDeclaration).parameters as List).iterator + var boolean l = true + while(it1.hasNext && l) { + l = it1.next.type.toString == it2.next.type.toString + } + + if (l) { + result = method as MethodDeclaration + } + } + } + result + } + + /** + * Determines whether targetVisibility is less visible than actualVisibility + * @param targetVisibility the visibility to be examined + * @param actualVisibility the visibility that targetVisibility will be compared to + * @return true, if targetVisibility is less visible than actualVisibility, false otherwise + */ + def protected static isLessVisible(Visibility targetVisibility, Visibility actualVisibility) { + if ((actualVisibility == Visibility.PUBLIC && (targetVisibility == Visibility.PRIVATE || targetVisibility == Visibility.PACKAGE || targetVisibility == Visibility.PROTECTED)) || + actualVisibility == Visibility.PROTECTED && (targetVisibility == Visibility.PRIVATE || targetVisibility == Visibility.PACKAGE) || + actualVisibility == Visibility.PACKAGE && (targetVisibility == Visibility.PRIVATE) ) { + true + } else { + false + } + } + + /** + * Determines whether targetType is a subclass of actualType. Both are ITypeBindings. + * @param targetType the targetType to be examined + * @param actualType the type that targetType will be compared to + * @return true, if targetType is a subclass of actualType, false otherwise + */ + def private static isSubClassOf(ITypeBinding targetType, ITypeBinding actualType) { + var tmp = targetType + var boolean l = targetType.isEqualTo(actualType) + while (tmp.superclass !== null && !l) { + tmp = tmp.superclass + l = tmp.isEqualTo(actualType) + } + return l + } + + /** + * Determines whether targetType is a subtype of actualType. Both are org.eclipse.jdt.core.dom.Type. + * @param targetType the targetType to be examined + * @param actualType the type that targetType will be compared to + * @return true, if targetType is a subtype of actualType, false otherwise + */ + def protected static isSubTypeOf(Type targetType, Type actualType) { + if (targetType.isPrimitiveType && actualType.isPrimitiveType) { + targetType.toString == actualType.toString + } else if (targetType.arrayType && actualType.arrayType) { + if ((targetType as ArrayType).getDimensions != (actualType as ArrayType).getDimensions ) { + false + } else { + if((targetType as ArrayType).elementType.isPrimitiveType && (actualType as ArrayType).elementType.isPrimitiveType) { + (targetType as ArrayType).elementType.toString == (actualType as ArrayType).elementType.toString + } else { + targetType.resolveBinding.isSubClassOf(actualType.resolveBinding) + } + } + } else if (targetType.simpleType && actualType.simpleType) { + targetType.resolveBinding.isSubClassOf(actualType.resolveBinding) + } else { + false + } + } + + /** + * + * @param name + * @param typeDecl + * @return + */ + def private static getTypeOfFieldOrVarDeclOfName(Name name, TypeDeclaration typeDecl) { + val binding = if (name instanceof QualifiedName) { + name.qualifier.resolveBinding + } else if (name instanceof SimpleName) { + name.resolveBinding + } + + val List result = newArrayList + typeDecl.bodyDeclarations.exists[ + val visitor = new ASTVisitor() { + public var boolean found = false + + override visit(SimpleName name) { + if(name.resolveBinding.isEqualTo(binding) && (Utils.getFieldDeclaration(name) !== null || Utils.getVariableDeclaration(name) !== null) ) { + found = true + if(Utils.getFieldDeclaration(name) !== null) { + result.add(Utils.getFieldDeclaration(name)) + } else { + result.add(Utils.getVariableDeclaration(name)) + } + return false + } + return true + } + } + (it as ASTNode).accept(visitor) + return visitor.found + ] + + if(!result.empty && result.get(0) instanceof FieldDeclaration) { + return (result.get(0) as FieldDeclaration).type + } else if (!result.empty && result.get(0) instanceof VariableDeclarationStatement) { + return (result.get(0) as VariableDeclarationStatement).type + } + } + + /** + * Gets the field the is referred by an ASTNode. + * Note: Only works if the reference is a SimpleName, and referring to a Field. Returns null otherwise. + * @param reference the ASTNode + * @return the referred FieldDeclaration + */ + def protected static referredField(ASTNode reference) { + if(reference instanceof SimpleName) { + val binding = (reference as SimpleName).resolveBinding + for(typeDecl : allTypeDeclarationInWorkSpace) { + for (declaration : typeDecl.bodyDeclarations) { + if(declaration instanceof FieldDeclaration && ((declaration as FieldDeclaration).fragments as List).exists[it.resolveBinding.isEqualTo(binding)] ) { + return declaration as FieldDeclaration + } + } + } + } + null + } + + /** + * Gets all references to a FieldDeclaration in one of the given TypeDeclaration's superclass, that can potentially get violated of it gets overridden. + * @param methodName the FieldDeclaration's fragment names + * @param typeDecl the TypeDeclaration + * @return all references that can potentially get violated if an override happens + */ + def static private referencesThatCanGetViolated(String fragmentName, TypeDeclaration typeDecl) { + val List references = newArrayList + var TypeDeclaration superclassWithSameField = allTypeDeclarationInWorkSpace.findFirst[it.resolveBinding.qualifiedName == typeDecl.resolveBinding.qualifiedName] + var boolean found = false + while(superclassWithSameField.hasSuperClass && !found) { + superclassWithSameField = superclassWithSameField.superClass + found = !isUniqueFieldIn(fragmentName, superclassWithSameField) + } + + if (!found) { + return references + } + + val fieldInSuperClass = superclassWithSameField.bodyDeclarations.findFirst[ + it instanceof FieldDeclaration && ((it as FieldDeclaration).fragments as List).exists[ + it.name.identifier == fragmentName + ] + ] as FieldDeclaration + + val List a = newArrayList + a.add(fieldInSuperClass) + for (t : allTypeDeclarationInWorkSpace) { + val refs = references(a, t) + for (r : refs) { + val refTypeDecl = Utils.getTypeDeclaration(r) + if (refTypeDecl.resolveBinding.isEqualTo(typeDecl.resolveBinding) || + refTypeDecl.resolveBinding.isSubClassOf(typeDecl.resolveBinding) || + (r.parent instanceof QualifiedName && (getTypeOfFieldOrVarDeclOfName(r.parent as QualifiedName, refTypeDecl).resolveBinding.isEqualTo(typeDecl.resolveBinding) || + getTypeOfFieldOrVarDeclOfName(r.parent as QualifiedName, refTypeDecl).resolveBinding.isSubClassOf(typeDecl.resolveBinding))) ) { + + references.add(r) + } + } + } + references + } + + /** + * Gets all references to a MethodDeclaration in one of the given TypeDeclaration's superclass, that can potentially get violated of it gets overridden. + * @param methodName the MethodDeclaration's name + * @param methodParameters the MethodDeclaration's parameters + * @param typeDecl the TypeDeclaration + * @return all references that can potentially get violated if an override happens + */ + def private static referencesThatCanGetViolated(String methodName, List methodParameters, TypeDeclaration typeDecl) { + val List references = newArrayList + var TypeDeclaration superclassWithSameMethod = allTypeDeclarationInWorkSpace.findFirst[it.resolveBinding.qualifiedName == typeDecl.resolveBinding.qualifiedName] + var boolean found = false + while(superclassWithSameMethod.hasSuperClass && !found) { + superclassWithSameMethod = superclassWithSameMethod.superClass + found = !isUniqueMethodIn(methodName, methodParameters, superclassWithSameMethod) + } + + if (!found) { + return references + } + + val methodInSuperClass = getMethodFromClass(methodName, methodParameters, superclassWithSameMethod) + val List a = newArrayList + a.add(methodInSuperClass) + for (t : allTypeDeclarationInWorkSpace) { + val refs = references(a, t) + for (r : refs) { + val refTypeDecl = Utils.getTypeDeclaration(r) + if (refTypeDecl.resolveBinding.isEqualTo(typeDecl.resolveBinding) || + refTypeDecl.resolveBinding.isSubClassOf(typeDecl.resolveBinding) || + ( (r.parent as MethodInvocation).expression !== null && (getTypeOfFieldOrVarDeclOfName((r.parent as MethodInvocation).expression as Name, refTypeDecl).resolveBinding.isEqualTo(typeDecl.resolveBinding) || + getTypeOfFieldOrVarDeclOfName((r.parent as MethodInvocation).expression as Name, refTypeDecl).resolveBinding.isSubClassOf(typeDecl.resolveBinding))) ) { + + references.add(r) + } + } + } + references + } + + /** + * Gets all the references to a FieldDeclaration, and separates them by their visibility. + * @param whichReferences decides if whether the public, or non-public references are returned + * @param fragmentNames the FieldDeclaration's fragment names + * @param targetTypeDecl the FieldDeclaration's TypeDeclaration + * @return if whichReferences equals "public", the references that can get accessed via public interface are returned (list of ASTNodes) + * if whichReferences equals "nonPublic", the references the cannot get accessed via public interface are returned (list of ASTNodes) + */ + def private static references(String whichReferences, List fragmentNames, TypeDeclaration targetTypeDecl) { + val List publicReferences = newArrayList + val List nonPublicReferences = newArrayList + + for(fragmentName : fragmentNames) { + val allReferences = referencesThatCanGetViolated(fragmentName, targetTypeDecl) + val Queue methodsToCheck = newLinkedList + + for(ref : allReferences) { + if(Utils.getMethodDeclaration(ref) !== null && Utils.getMethodDeclaration(ref).visibility == Visibility.PUBLIC) { + publicReferences.add(ref) + } else if (Utils.getMethodDeclaration(ref) !== null && Utils.getMethodDeclaration(ref).visibility != Visibility.PUBLIC){ + methodsToCheck.add(Utils.getMethodDeclaration(ref)) + + while(!methodsToCheck.empty) { + val method = methodsToCheck.remove + var List methodRefs = newArrayList + for (t : allTypeDeclarationInWorkSpace) { + val List tmp = newArrayList + tmp.add(method) + methodRefs.addAll(references(tmp, t)) + } + if(!methodRefs.empty) { + for(methodRef : methodRefs) { + if(Utils.getMethodDeclaration(methodRef) !== null && Utils.getMethodDeclaration(methodRef).visibility == Visibility.PUBLIC) { + publicReferences.add(ref) + } else if (Utils.getMethodDeclaration(methodRef) !== null && Utils.getMethodDeclaration(methodRef).visibility != Visibility.PUBLIC) { + methodsToCheck.add(Utils.getMethodDeclaration(methodRef)) + } else if(Utils.getMethodDeclaration(methodRef) === null) { + publicReferences.add(ref) + } + } + } else { + if(!nonPublicReferences.exists[it == ref]) { + nonPublicReferences.add(ref) + } + } + } + } else if(Utils.getMethodDeclaration(ref) === null) { + publicReferences.add(ref) + } + } + } + + if(whichReferences == "public") { + return publicReferences + } else if(whichReferences == "nonPublic") { + return nonPublicReferences + } + } + + /** + * Gets all the references to a MethodDeclaration, and separates them by their visibility. + * @param whichReferences decides if whether the public, or non-public references are returned + * @param methodName the MethodDeclaration's name + * @param mathodParameters the MethodDeclaration's parameters + * @param targetTypeDecl the MethodDeclaration's TypeDeclaration + * @return if whichReferences equals "public", the references that can get accessed via public interface are returned (list of ASTNodes) + * if whichReferences equals "nonPublic", the references the cannot get accessed via public interface are returned (list of ASTNodes) + */ + def private static references(String whichReferences, String methodName, List methodParameters,TypeDeclaration targetTypeDecl) { + val List publicReferences = newArrayList + val List nonPublicReferences = newArrayList + + val allReferences = referencesThatCanGetViolated(methodName, methodParameters, targetTypeDecl) + val Queue methodsToCheck = newLinkedList + + for(ref : allReferences) { + if(Utils.getMethodDeclaration(ref) !== null && Utils.getMethodDeclaration(ref).visibility == Visibility.PUBLIC) { + publicReferences.add(ref) + } else if (Utils.getMethodDeclaration(ref) !== null && Utils.getMethodDeclaration(ref).visibility != Visibility.PUBLIC){ + methodsToCheck.add(Utils.getMethodDeclaration(ref)) + + while(!methodsToCheck.empty) { + val method = methodsToCheck.remove + var List methodRefs = newArrayList + for (t : allTypeDeclarationInWorkSpace) { + val List tmp = newArrayList + tmp.add(method) + methodRefs.addAll(references(tmp, t)) + } + + if(!methodRefs.empty) { + for(methodRef : methodRefs) { + if(Utils.getMethodDeclaration(methodRef) !== null && Utils.getMethodDeclaration(methodRef).visibility == Visibility.PUBLIC) { + publicReferences.add(ref) + } else if (Utils.getMethodDeclaration(methodRef) !== null && Utils.getMethodDeclaration(methodRef).visibility != Visibility.PUBLIC) { + methodsToCheck.add(Utils.getMethodDeclaration(methodRef)) + } else if(Utils.getMethodDeclaration(methodRef) === null) { + publicReferences.add(ref) + } + } + } else { + if(!nonPublicReferences.exists[it == ref]) { + nonPublicReferences.add(ref) + } + } + } + } else if(Utils.getMethodDeclaration(ref) === null) { + publicReferences.add(ref) + } + } + + if(whichReferences == "public") { + return publicReferences + } else if(whichReferences == "nonPublic") { + return nonPublicReferences + } + } + + /** + * Gets all references to a FieldDeclaration that cannot get accessed via public interface. + * @param fragmentNames the FieldDeclaration's fragment names + * @param targetTypeDeclaration the FieldDeclaration's TypeDeclaration + * @return list of ASTNodes + */ + def protected static nonPublicReferences(List fragmentNames, TypeDeclaration targetTypeDecl) { + references("nonPublic", fragmentNames, targetTypeDecl) + } + + /** + * Gets all subclasses of a TypeDeclaration. + * @param typeDecl the TypeDeclaration + * @return all subclasses of typeDecl (list of org.eclipse.jdt.core.dom.TypeDeclaration) + */ + def protected static getAllSubClasses(TypeDeclaration typeDecl) { + val binding = typeDecl.resolveBinding + val List subClasses = newArrayList + + allTypeDeclarationInWorkSpace.forEach[ + if(it.resolveBinding.isSubClassOf(binding) && typeDecl != it) { + subClasses.add(it) + } + ] + subClasses + } + + /** + * Gets all MethodDeclaration with the same name and parameter types as the given name and parameter types from the given TypeDeclaration's subclasses. + * @param methodName the identifier of the method + * @param methodParameters the list of parameters of the method + * @param targetTypeDeclaration the TypeDeclaration + * @return list of MethodDeclarations + */ + def protected static overridesOf(String methodName, List methodParameters, TypeDeclaration targetTypeDeclaration) { + val subClasses = targetTypeDeclaration.allSubClasses + val List overriddenMethods = newArrayList + subClasses.forEach[ + if(!isUniqueMethodIn(methodName, methodParameters, it)) { + overriddenMethods.add(getMethodFromClass(methodName, methodParameters, it)) + } + ] + overriddenMethods + } } diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/api/ClassRefactoring.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/api/ClassRefactoring.xtend new file mode 100644 index 0000000..36eb60a --- /dev/null +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/api/ClassRefactoring.xtend @@ -0,0 +1,281 @@ +package hu.elte.refjava.api + +import hu.elte.refjava.api.patterns.ASTBuilder +import hu.elte.refjava.api.patterns.PatternMatcher +import hu.elte.refjava.api.patterns.PatternParser +import hu.elte.refjava.api.patterns.Utils +import hu.elte.refjava.lang.refJava.PMethodDeclaration +import java.util.List +import org.eclipse.jdt.core.dom.ASTNode +import org.eclipse.jdt.core.dom.FieldDeclaration +import org.eclipse.jdt.core.dom.MethodDeclaration +import org.eclipse.jdt.core.dom.TypeDeclaration +import org.eclipse.jdt.core.dom.VariableDeclarationFragment +import org.eclipse.jface.text.Document +import org.eclipse.jface.text.IDocument + +import static hu.elte.refjava.api.Check.* +import hu.elte.refjava.lang.refJava.Visibility + +class ClassRefactoring implements Refactoring { + + List target + IDocument document + + val PatternMatcher matcher + val ASTBuilder builder + val String matchingString + val String replacementString + val RefactoringType refactoringType + protected String targetString + protected String definitionString + protected String matchingTypeReferenceString + protected String replacementTypeReferenceString + protected String targetTypeReferenceString + protected String definitionTypeReferenceString + List replacement + + enum RefactoringType { + NEW_METHOD, + METHOD_LIFT, + FIELD_LIFT + } + + protected new(String matchingPatternString, String replacementPatternString) { + nameBindings.clear + typeBindings.clear + parameterBindings.clear + visibilityBindings.clear + argumentBindings.clear + setMetaVariables + this.matcher = new PatternMatcher(PatternParser.parse(matchingPatternString)) + this.builder = new ASTBuilder(PatternParser.parse(replacementPatternString)) + this.matchingString = matchingPatternString + this.replacementString = replacementPatternString + this.refactoringType = if(replacementPatternString != "nothing") { + RefactoringType.NEW_METHOD + } else { + if(PatternParser.parse(matchingPatternString).patterns.head instanceof PMethodDeclaration) { + RefactoringType.METHOD_LIFT + } else { + RefactoringType.FIELD_LIFT + } + } + + if (replacementString == "nothing" && definitionString == "target") { + this.definitionString = matchingString + this.definitionTypeReferenceString = matchingTypeReferenceString + } + } + + override init(List target, IDocument document, List allTypeDeclInWorkspace) { + this.target = target + this.document = document + Check.allTypeDeclarationInWorkSpace = allTypeDeclInWorkspace + } + + override apply() { + return if(!safeTargetCheck) { + Status.TARGET_MATCH_FAILED + } else if (!safeMatch) { + Status.MATCH_FAILED + } else if (!safeCheck) { + Status.CHECK_FAILED + } else if (!safeBuild) { + Status.BUILD_FAILED + } else if (!safeReplace) { + Status.REPLACEMENT_FAILED + } else { + Status.SUCCESS + } + } + + def private safeMatch() { + try { + if (!matcher.match(target, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, matchingTypeReferenceString)) { + return false + } + target = matcher.modifiedTarget.toList + bindings.putAll(matcher.bindings) + true + } catch (Exception e) { + println(e) + false + } + } + + def protected void setMetaVariables() { + //empty + } + + def protected safeTargetCheck() { + true + } + + def protected targetCheck(String targetPatternString) { + try { + this.targetString = targetPatternString + if(!matcher.match(PatternParser.parse(targetPatternString), target, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, targetTypeReferenceString)) { + return false + } + bindings.putAll(matcher.bindings) + true + } catch (Exception e) { + println(e) + false + } + } + + def private safeCheck() { + try { + check() + } catch (Exception e) { + println(e) + false + } + } + + def protected check() { + + if(refactoringType == RefactoringType.NEW_METHOD) { + + val iCompUnit = Utils.getICompilationUnit(target.head) + val compUnit = Utils.parseSourceCode(iCompUnit) + compUnit.recordModifications + + val definitionPattern = PatternParser.parse(definitionString) + val newMethod = builder.build(definitionPattern, compUnit.AST, bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, definitionTypeReferenceString).head as MethodDeclaration + val targetClass = compUnit.types.findFirst[(it as TypeDeclaration).resolveBinding.qualifiedName == Utils.getTypeDeclaration(target.head).resolveBinding.qualifiedName ] as TypeDeclaration + targetClass.bodyDeclarations.add(newMethod) + Utils.applyChanges(compUnit, document) + + val compUnit2 = Utils.parseSourceCode(iCompUnit) + val targetClass2 = compUnit2.types.findFirst[(it as TypeDeclaration).resolveBinding.qualifiedName == Utils.getTypeDeclaration(target.head).resolveBinding.qualifiedName] as TypeDeclaration + val newMethodInClass = targetClass2.bodyDeclarations.last + val List definition = newArrayList + definition.add(newMethodInClass) + + val overrideCheck = if(isOverrideIn(getMethodName(definition), parameters(definition), enclosingClass(target))) { + !isLessVisible(visibility(definition), visibility(overridenMethodFrom(getMethodName(definition), parameters(definition), enclosingClass(target)))) + && isSubTypeOf(type(definition), type(overridenMethodFrom(getMethodName(definition), parameters(definition), enclosingClass(target)))) + && visibility(overridenMethodFrom(getMethodName(definition), parameters(definition), enclosingClass(target))) != Visibility.PUBLIC + && publicReferences(getMethodName(definition), parameters(definition), enclosingClass(target)).empty } else { true } + + val safeCheck = isUniqueMethodIn(getMethodName(definition), parameters(definition), enclosingClass(target)) + && overrideCheck + && overridesOf(getMethodName(definition), parameters(definition), enclosingClass(target)).forall[ + !isLessVisible(visibility(it), visibility(definition)) && + isSubTypeOf(type(it), type(definition))] + + compUnit2.recordModifications + targetClass2.bodyDeclarations.remove(newMethodInClass) + Utils.applyChanges(compUnit2, document) + safeCheck + + } else { + if(refactoringType == RefactoringType.METHOD_LIFT) { + + val privateCheck = if (isPrivate(target)) { references(target, enclosingClass(target)).empty } else { true } + val overrideCheck = if(isOverrideIn(getMethodName(target), parameters(target), superClass(enclosingClass(target)))) { + !isLessVisible(visibility(target), visibility(overridenMethodFrom(getMethodName(target), parameters(target), superClass(enclosingClass(target))))) + && isSubTypeOf(type(target), type(overridenMethodFrom(getMethodName(target), parameters(target), superClass(enclosingClass(target))))) + && visibility(overridenMethodFrom(getMethodName(target), parameters(target), superClass(enclosingClass(target)))) != Visibility.PUBLIC + && publicReferences(getMethodName(target), parameters(target), superClass(enclosingClass(target))).empty } else { true } + + return hasSuperClass(enclosingClass(target)) + && privateCheck + && accessedFieldsOfEnclosingClass(target, enclosingClass(target)).empty + && accessedMethodsOfEnclosingClass(target, enclosingClass(target)).empty + && isUniqueMethodIn(getMethodName(target), parameters(target), superClass(enclosingClass(target))) + && overrideCheck + && overridesOf(getMethodName(target), parameters(target), superClass(enclosingClass(target))).forall[ + !isLessVisible(visibility(it), visibility(target)) && + isSubTypeOf(type(it), type(target))] + + } else if (refactoringType == RefactoringType.FIELD_LIFT) { + + val privateCheck = if (isPrivate(target)) { references(target, enclosingClass(target)).empty } else { true } + return hasSuperClass(enclosingClass(target)) + && privateCheck + && isUniqueFieldIn(getFragmentNames(target), superClass(enclosingClass(target))) + && publicReferences(getFragmentNames(target), superClass(enclosingClass(target))).empty + && nonPublicReferences(getFragmentNames(target), superClass(enclosingClass(target))).forall [ + isSubTypeOf(Check.type(target), type(referredField(it))) ] + } + false + } + } + + def private safeBuild() { + try { + if(refactoringType == RefactoringType.NEW_METHOD) { + val replacementPattern = PatternParser.parse(replacementString) + replacement = builder.build(replacementPattern, target.head.AST, bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, definitionTypeReferenceString) + } + true + } catch (Exception e) { + println(e) + false + } + } + + def private safeReplace() { + try { + //we only need to replace if we creating a new method + if (refactoringType == RefactoringType.NEW_METHOD) { + val rewrite = builder.rewrite + target.tail.forEach[rewrite.remove(it, null)] + + val group = rewrite.createGroupNode(replacement) + rewrite.replace( target.head, group, null) + var edits = rewrite.rewriteAST(document, null) + edits.apply(document) + } + + val targetTypeDeclaration = Utils.getTypeDeclaration(target.head) + var superClass = superClass(targetTypeDeclaration) + val superCompUnit = Utils.getCompilationUnit(superClass) + val superICompUnit = Utils.getICompilationUnit(superCompUnit) + + val targetICompUnit = Utils.getICompilationUnit(target.head) + val targetCompUnit = Utils.parseSourceCode(targetICompUnit) + targetCompUnit.recordModifications + + val definitionPattern = PatternParser.parse(definitionString) + var objectToInsertOrMove = builder.build(definitionPattern, targetCompUnit.AST, bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, definitionTypeReferenceString).head + val targetClass = targetCompUnit.types.findFirst[(it as TypeDeclaration).name.identifier == targetTypeDeclaration.name.identifier] as TypeDeclaration + + if(refactoringType == RefactoringType.NEW_METHOD) { + targetClass.bodyDeclarations.add(objectToInsertOrMove) + } else { + if(superICompUnit != targetICompUnit) { + superCompUnit.recordModifications + objectToInsertOrMove = builder.build(definitionPattern, superCompUnit.AST , bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, definitionTypeReferenceString).head + } else { + superClass = targetCompUnit.types.findFirst[(it as TypeDeclaration).resolveBinding.isEqualTo(targetClass.superclassType.resolveBinding)] + } + + val methodOrFieldToDelete = if(refactoringType == RefactoringType.METHOD_LIFT){ + getMethodFromClass(getMethodName(target), parameters(target), targetClass) + } else if (refactoringType == RefactoringType.FIELD_LIFT) { + targetClass.bodyDeclarations.findFirst[it instanceof FieldDeclaration && + ((it as FieldDeclaration).fragments.head as VariableDeclarationFragment).name.identifier == ((target.head as FieldDeclaration).fragments.head as VariableDeclarationFragment).name.identifier] + } + + superClass.bodyDeclarations.add(objectToInsertOrMove) + targetClass.bodyDeclarations.remove(methodOrFieldToDelete) + + if(superICompUnit != targetICompUnit) { + val superDocument = new Document(superICompUnit.source) + Utils.applyChanges(superCompUnit, superDocument) + superICompUnit.getBuffer.setContents(superDocument.get) + } + } + Utils.applyChanges(targetCompUnit, document) + true + } catch (Exception e) { + println(e) + return false + } + } +} \ No newline at end of file diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/api/LambdaRefactoring.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/api/LambdaRefactoring.xtend new file mode 100644 index 0000000..cb4f5ca --- /dev/null +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/api/LambdaRefactoring.xtend @@ -0,0 +1,221 @@ +package hu.elte.refjava.api + +import hu.elte.refjava.api.patterns.ASTBuilder +import hu.elte.refjava.api.patterns.PatternMatcher +import hu.elte.refjava.api.patterns.PatternParser +import hu.elte.refjava.api.patterns.Utils +import hu.elte.refjava.lang.refJava.PConstructorCall +import hu.elte.refjava.lang.refJava.PMemberFeatureCall +import hu.elte.refjava.lang.refJava.PMetaVariable +import java.util.List +import org.eclipse.jdt.core.dom.ASTNode +import org.eclipse.jdt.core.dom.ExpressionStatement +import org.eclipse.jdt.core.dom.TypeDeclaration +import org.eclipse.jface.text.Document +import org.eclipse.jface.text.IDocument + +import static hu.elte.refjava.api.Check.* + +class LambdaRefactoring implements Refactoring { + + List target + List allTypeDecl + IDocument document + + val PatternMatcher matcher + val ASTBuilder builder + val String matchingString + val String replacementString + val RefactoringType refactoringType + protected TypeDeclaration interfaceToModify + protected String definitionString + protected String matchingTypeReferenceString + protected String replacementTypeReferenceString + protected String targetTypeReferenceString + protected String definitionTypeReferenceString + List replacement + + enum RefactoringType { + MODIFICATION, + NEW + } + + protected new(String matchingPatternString, String replacementPatternString) { + nameBindings.clear + typeBindings.clear + parameterBindings.clear + visibilityBindings.clear + argumentBindings.clear + setMetaVariables + this.matcher = new PatternMatcher(PatternParser.parse(matchingPatternString)) + this.builder = new ASTBuilder(PatternParser.parse(replacementPatternString)) + this.matchingString = matchingPatternString + this.replacementString = replacementPatternString + + val matchingPatterns = PatternParser.parse(matchingPatternString).patterns + if (matchingPatterns.exists[Utils.isValidLambdaExpression(it)]) { + this.refactoringType = RefactoringType.MODIFICATION + } else { + this.interfaceToModify = null + this.refactoringType = RefactoringType.NEW + } + } + + override init(List target, IDocument document, List allTypeDeclInWorkspace) { + this.target = target + this.document = document + this.allTypeDecl = allTypeDeclInWorkspace + Check.allTypeDeclarationInWorkSpace = allTypeDeclInWorkspace + + } + + override apply() { + return if(!safeTargetCheck) { + Status.TARGET_MATCH_FAILED + } else if (!safeMatch) { + Status.MATCH_FAILED + } else if (!safeCheck) { + Status.CHECK_FAILED + } else if (!safeBuild) { + Status.BUILD_FAILED + } else if (!safeReplace) { + Status.REPLACEMENT_FAILED + } else { + Status.SUCCESS + } + } + + def private safeMatch() { + try { + if (!matcher.match(target, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, matchingTypeReferenceString)) { + return false + } + target = matcher.modifiedTarget.toList + bindings.putAll(matcher.bindings) + true + } catch (Exception e) { + println(e) + false + } + } + + def protected void setMetaVariables() { + //empty + } + + def protected safeTargetCheck() { + true + } + + def protected targetCheck(String targetPatternString) { + try { + if(!matcher.match(PatternParser.parse(targetPatternString), target, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, targetTypeReferenceString)) { + return false + } + bindings.putAll(matcher.bindings) + true + } catch (Exception e) { + println(e) + false + } + } + + def private safeCheck() { + try { + check() + } catch (Exception e) { + println(e) + false + } + } + + def protected check() { + + if(refactoringType == RefactoringType.NEW) { + val iCompUnit = Utils.getICompilationUnit(target.head) + val compUnit = Utils.parseSourceCode(iCompUnit) + + val replacementPattern = PatternParser.parse(replacementString) + val replacementLambdaExpression = replacementPattern.patterns.head + if (((replacementLambdaExpression as PMemberFeatureCall).memberCallTarget as PConstructorCall).metaName !== null) { + val metaVarName = (((replacementLambdaExpression as PMemberFeatureCall).memberCallTarget as PConstructorCall).metaName as PMetaVariable).name + nameBindings.put(metaVarName, generateNewName) + } + + val newLambdaExpression = builder.build(replacementPattern, compUnit.AST, bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, replacementTypeReferenceString).head as ExpressionStatement + return isFresh(getLambdaName(newLambdaExpression)) + && (lambdaVariableAssignments(getLambdaBody(newLambdaExpression)).forall[isDeclaredIn(it, getLambdaBody(newLambdaExpression))]) + + } else { + + val matchingLambdaExpression = PatternParser.parse(matchingString).patterns.get(0) as PMemberFeatureCall + val interfaceName = if((matchingLambdaExpression.memberCallTarget as PConstructorCall).metaName !== null) { + nameBindings.get(((matchingLambdaExpression.memberCallTarget as PConstructorCall).metaName as PMetaVariable).name) + } else { + (matchingLambdaExpression.memberCallTarget as PConstructorCall).name + } + this.interfaceToModify = allTypeDecl.findFirst[it.name.identifier == interfaceName] + + return references(interfaceToModify).size == 1 && + contains(references(interfaceToModify), target) + } + } + + def private safeBuild() { + try { + replacement = builder.build(target.head.AST, bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, replacementTypeReferenceString) + true + } catch (Exception e) { + println(e) + false + } + } + + def private safeReplace() { + try { + val rewrite = builder.rewrite + target.tail.forEach[rewrite.remove(it, null)] + val group = rewrite.createGroupNode(replacement) + rewrite.replace(target.head, group, null) + var edits = rewrite.rewriteAST(document, null) + edits.apply(document) + + val iCompUnit= Utils.getICompilationUnit(target.head) + val compUnit = Utils.parseSourceCode(iCompUnit) + compUnit.recordModifications + + val replacementLambdaExpression = PatternParser.parse(replacementString).patterns.get(0) as PMemberFeatureCall + if(refactoringType == RefactoringType.NEW) { + //if we are defining a new lambda expression, we just simply add a new interface to the target document + val newInterface = builder.buildNewInterface(replacementLambdaExpression, compUnit.AST, bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, replacementTypeReferenceString) + compUnit.types.add(newInterface) + } else { + //if we modifying an existing lambda expression, we have to find out where is the existing interface declaration on the workspace + val interfaceCompUnit = Utils.getCompilationUnit(interfaceToModify) + val interfaceICompUnit = Utils.getICompilationUnit(interfaceCompUnit) + if(interfaceICompUnit != iCompUnit) { + //if the interface's document isn't the same as the target document, we are going to remove that interface from that document and then add the new + interfaceCompUnit.recordModifications + val newInterface = builder.buildNewInterface(replacementLambdaExpression, interfaceCompUnit.AST, bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, replacementTypeReferenceString) + interfaceCompUnit.types.remove(interfaceToModify) + interfaceCompUnit.types.add(newInterface) + + val interfaceDocument = new Document(interfaceICompUnit.source) + Utils.applyChanges(interfaceCompUnit, interfaceDocument) + interfaceICompUnit.getBuffer.setContents(interfaceDocument.get) + } else { + //if the interface's document is the same as the target document, we just simply remove the interface from the target document and then add the new + val newInterface = builder.buildNewInterface(replacementLambdaExpression, compUnit.AST, bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, replacementTypeReferenceString) + val interfaceToRemove = compUnit.types.findFirst[(it as TypeDeclaration).resolveBinding.qualifiedName == interfaceToModify.resolveBinding.qualifiedName] as TypeDeclaration + compUnit.types.remove(interfaceToRemove) + compUnit.types.add(newInterface) + } + } + Utils.applyChanges(compUnit, document) + true + } catch (Exception e) { + println(e) + return false + } + } +} \ No newline at end of file diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/api/LocalRefactoring.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/api/LocalRefactoring.xtend index cb53f41..4209c6e 100644 --- a/hu.elte.refjava.lang/src/hu/elte/refjava/api/LocalRefactoring.xtend +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/api/LocalRefactoring.xtend @@ -4,10 +4,12 @@ import hu.elte.refjava.api.patterns.ASTBuilder import hu.elte.refjava.api.patterns.PatternMatcher import hu.elte.refjava.api.patterns.PatternParser import java.util.List -import java.util.Map import org.eclipse.jdt.core.dom.ASTNode +import org.eclipse.jdt.core.dom.TypeDeclaration import org.eclipse.jface.text.IDocument +import static hu.elte.refjava.api.Check.* + class LocalRefactoring implements Refactoring { List target @@ -15,15 +17,24 @@ class LocalRefactoring implements Refactoring { val PatternMatcher matcher val ASTBuilder builder - protected val Map> bindings = newHashMap + protected String matchingTypeReferenceString + protected String replacementTypeReferenceString + protected String targetTypeReferenceString + protected String definitionTypeReferenceString List replacement - protected new(String matchingPatternString, String replacementPatternString) { - matcher = new PatternMatcher(PatternParser.parse(matchingPatternString)) - builder = new ASTBuilder(PatternParser.parse(replacementPatternString)) + new(String matchingPatternString, String replacementPatternString) { + nameBindings.clear + typeBindings.clear + parameterBindings.clear + visibilityBindings.clear + argumentBindings.clear + setMetaVariables + this.matcher = new PatternMatcher(PatternParser.parse(matchingPatternString)) + this.builder = new ASTBuilder(PatternParser.parse(replacementPatternString)) } - override init(List target, IDocument document) { + override init(List target, IDocument document, List allTypeDeclInWorkspace) { this.target = target this.document = document } @@ -43,51 +54,58 @@ class LocalRefactoring implements Refactoring { } def private safeMatch() { - if (!matcher.match(target)) { + if (!matcher.match(target, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, matchingTypeReferenceString)) { return false } - bindings.putAll(matcher.bindings) - return true + true } def private safeCheck() { try { check() } catch (Exception e) { + println(e) false } } + + def protected void setMetaVariables() { + //empty + } def protected check() { - Check.isInsideBlock(target) + isInsideBlock(target) } def private safeBuild() { try { - replacement = builder.build(target.head.AST, bindings) + replacement = builder.build(target.head.AST, bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, replacementTypeReferenceString) + true } catch (Exception e) { - return false + println(e) + false } - - return true } def private safeReplace() { try { val rewrite = builder.rewrite target.tail.forEach[rewrite.remove(it, null)] - - val group = rewrite.createGroupNode(replacement) + + val group = if (replacement.size == 0) { + null + } else { + rewrite.createGroupNode(replacement) + } + rewrite.replace(target.head, group, null) - val edits = rewrite.rewriteAST(document, null) edits.apply(document) + true } catch (Exception e) { - return false + println(e) + false } - - return true } - } diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/api/Refactoring.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/api/Refactoring.xtend index d5d6230..04c4428 100644 --- a/hu.elte.refjava.lang/src/hu/elte/refjava/api/Refactoring.xtend +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/api/Refactoring.xtend @@ -1,21 +1,36 @@ package hu.elte.refjava.api +import hu.elte.refjava.lang.refJava.Visibility import java.util.List +import java.util.Map import org.eclipse.jdt.core.dom.ASTNode +import org.eclipse.jdt.core.dom.Expression +import org.eclipse.jdt.core.dom.SingleVariableDeclaration +import org.eclipse.jdt.core.dom.Type +import org.eclipse.jdt.core.dom.TypeDeclaration import org.eclipse.jface.text.IDocument interface Refactoring { - + + val Map> bindings = newHashMap + val Map nameBindings = newHashMap + val Map typeBindings = newHashMap + val Map> parameterBindings = newHashMap + val Map visibilityBindings = newHashMap + val Map> argumentBindings = newHashMap + + enum Status { SUCCESS, MATCH_FAILED, CHECK_FAILED, BUILD_FAILED, - REPLACEMENT_FAILED + REPLACEMENT_FAILED, + TARGET_MATCH_FAILED } - - def void init(List target, IDocument document) {} + + def void init(List target, IDocument document, List allTypeDeclInWorkspace) def Status apply() -} +} \ No newline at end of file diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/ASTBuilder.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/ASTBuilder.xtend index d321ee7..9670170 100644 --- a/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/ASTBuilder.xtend +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/ASTBuilder.xtend @@ -1,23 +1,45 @@ package hu.elte.refjava.api.patterns +import hu.elte.refjava.api.Check import hu.elte.refjava.lang.refJava.PBlockExpression +import hu.elte.refjava.lang.refJava.PConstructorCall import hu.elte.refjava.lang.refJava.PExpression +import hu.elte.refjava.lang.refJava.PFeatureCall +import hu.elte.refjava.lang.refJava.PMemberFeatureCall import hu.elte.refjava.lang.refJava.PMetaVariable +import hu.elte.refjava.lang.refJava.PMethodDeclaration +import hu.elte.refjava.lang.refJava.PNothingExpression +import hu.elte.refjava.lang.refJava.PTargetExpression +import hu.elte.refjava.lang.refJava.PVariableDeclaration import hu.elte.refjava.lang.refJava.Pattern +import hu.elte.refjava.lang.refJava.Visibility import java.util.List import java.util.Map +import java.util.Queue import org.eclipse.jdt.core.dom.AST import org.eclipse.jdt.core.dom.ASTNode +import org.eclipse.jdt.core.dom.Block +import org.eclipse.jdt.core.dom.Expression +import org.eclipse.jdt.core.dom.MethodDeclaration +import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword +import org.eclipse.jdt.core.dom.SingleVariableDeclaration +import org.eclipse.jdt.core.dom.Type import org.eclipse.jdt.core.dom.rewrite.ASTRewrite +import hu.elte.refjava.lang.refJava.PReturnExpression class ASTBuilder { - val Pattern pattern - + Pattern pattern AST ast ASTRewrite rewrite Map> bindings - + Map nameBindings + Map typeBindings + Map> parameterBindings + Map visibilityBindings + Map> argumentBindings + Queue typeReferenceQueue + new(Pattern pattern) { this.pattern = pattern } @@ -25,15 +47,81 @@ class ASTBuilder { def getRewrite() { rewrite } - - def build(AST ast, Map> bindings) { + + def build(Pattern pattern, AST ast, Map> bindings, Map nameBindings, Map typeBindings, Map> parameterBindings, Map visibilityBindings, Map> argumentBindings, String typeRefString) { + this.pattern = pattern + build(ast, bindings, nameBindings, typeBindings, parameterBindings, visibilityBindings, argumentBindings, typeRefString) + } + + def build(AST ast, Map> bindings, Map nameBindings, Map typeBindings, Map> parameterBindings, Map visibilityBindings, Map> argumentBindings, String typeRefString) { this.ast = ast this.rewrite = ASTRewrite.create(ast) this.bindings = bindings - - return pattern.patterns.doBuildPatterns + this.nameBindings = nameBindings + this.typeBindings = typeBindings + this.parameterBindings = parameterBindings + this.visibilityBindings = visibilityBindings + this.argumentBindings = argumentBindings + + if(typeRefString !== null) { + val tmp = typeRefString.split("\\|") + this.typeReferenceQueue = newLinkedList + this.typeReferenceQueue.addAll(tmp) + } + + if(pattern.patterns.head instanceof PNothingExpression) { + val List emptyList = newArrayList + emptyList + } else { + pattern.patterns.doBuildPatterns + } } - + + def buildNewInterface(PMemberFeatureCall lambdaExpr, AST ast, Map> bindings, Map nameBindings, Map typeBindings, Map> parameterBindings, Map visibilityBindings, Map> argumentBindings, String typeRefString){ + this.ast = ast + this.bindings = bindings + this.nameBindings = nameBindings + this.typeBindings = typeBindings + this.parameterBindings = parameterBindings + this.visibilityBindings = visibilityBindings + this.argumentBindings = argumentBindings + + val newInterface = ast.newTypeDeclaration + newInterface.interface = true + + if((lambdaExpr.memberCallTarget as PConstructorCall).metaName !== null) { + val name = ((lambdaExpr.memberCallTarget as PConstructorCall).metaName as PMetaVariable).name + if (nameBindings.get(name) === null) { + newInterface.name.identifier = Check.generateNewName() + } else { + newInterface.name.identifier = nameBindings.get(name) + } + } else { + newInterface.name.identifier = (lambdaExpr.memberCallTarget as PConstructorCall).name + } + + if(typeRefString !== null) { + val tmp = typeRefString.split("\\|") + this.typeReferenceQueue = newLinkedList + this.typeReferenceQueue.addAll(tmp) + } + + val lambdaExpressionBody = (lambdaExpr.memberCallTarget as PConstructorCall).elements + val newInterfaceBodyDeclarations = lambdaExpressionBody.doBuildPatterns + + newInterfaceBodyDeclarations.forEach[ + if(it instanceof MethodDeclaration) { + it.body = null + } + ] + + newInterface.bodyDeclarations.addAll(newInterfaceBodyDeclarations.filter[it instanceof MethodDeclaration]) + newInterface + } + + /////////////////////// + // doBuild overloads // + /////////////////////// def private dispatch doBuild(PMetaVariable metaVar) { val binding = bindings.get(metaVar.name) if (!binding.empty) { @@ -41,18 +129,259 @@ class ASTBuilder { rewrite.createGroupNode(copies) } } - - def private dispatch ASTNode doBuild(PBlockExpression blockPattern) { + + def private dispatch doBuild(PTargetExpression targetExpr) { + val binding = bindings.get("target") + if (!binding.empty) { + val copies = binding.map[ASTNode.copySubtree(ast, it)] + rewrite.createGroupNode(copies) + } + } + + //constructor call builder + def private dispatch doBuild(PConstructorCall constCall) { + val class = ast.newClassInstanceCreation + + //adding constructor call name + if (constCall.metaName !== null) { + val name = (constCall.metaName as PMetaVariable).name + class.type = ast.newSimpleType(ast.newName(nameBindings.get(name) ) ) + } else { + class.type = ast.newSimpleType(ast.newName(constCall.name) ) + } + + //adding constructor call anonymous class declaration (body) + if(constCall.anonInstance) { + val anonClass = ast.newAnonymousClassDeclaration + val buildDeclarations = constCall.elements.doBuildPatterns + anonClass.bodyDeclarations.addAll(buildDeclarations) + class.anonymousClassDeclaration = anonClass + } + + class + } + + //method declaration builder + def private dispatch ASTNode doBuild(PMethodDeclaration methodDecl) { + val method = ast.newMethodDeclaration + + //adding method name + if (methodDecl.prefix.metaName !== null) { + val name = (methodDecl.prefix.metaName as PMetaVariable).name + method.name.identifier = nameBindings.get(name) + } else { + method.name.identifier = methodDecl.prefix.name + } + + //adding method visibility + if (methodDecl.prefix.metaVisibility !== null) { + val metaVarName = (methodDecl.prefix.metaVisibility as PMetaVariable).name + switch visibilityBindings.get(metaVarName) { + case PUBLIC: method.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD)) + case PRIVATE: method.modifiers().add(ast.newModifier(ModifierKeyword.PRIVATE_KEYWORD)) + case PROTECTED: method.modifiers().add(ast.newModifier(ModifierKeyword.PROTECTED_KEYWORD)) + default: {} + } + } else { + switch methodDecl.prefix.visibility { + case PUBLIC: method.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD)) + case PRIVATE: method.modifiers().add(ast.newModifier(ModifierKeyword.PRIVATE_KEYWORD)) + case PROTECTED: method.modifiers().add(ast.newModifier(ModifierKeyword.PROTECTED_KEYWORD)) + default: {} + } + } + + //adding method return type + if(methodDecl.prefix.type !== null) { + method.returnType2 = Utils.getTypeFromId(typeReferenceQueue.remove, ast) + } else { + val name = (methodDecl.prefix.metaType as PMetaVariable).name + method.returnType2 = ASTNode.copySubtree(ast, typeBindings.get(name)) as Type + } + + //adding method parameters + if (methodDecl.parameters.size > 0) { + for(argument : methodDecl.parameters) { + val typeName = typeReferenceQueue.remove + val methodParameterDeclaration = ast.newSingleVariableDeclaration + methodParameterDeclaration.type = Utils.getTypeFromId(typeName, ast) + methodParameterDeclaration.name.identifier = argument.name + method.parameters.add(methodParameterDeclaration) + } + } else if (methodDecl.metaParameters !== null) { + val parameterList = parameterBindings.get((methodDecl.metaParameters as PMetaVariable).name) + method.parameters.addAll(ASTNode.copySubtrees(ast, parameterList)) + } + + //adding method body + val block = ast.newBlock + val methodBody = (methodDecl.body as PBlockExpression).expressions + var List methodBodyList = newArrayList + for(element : methodBody) { + if(element instanceof PMetaVariable) { + val binding = bindings.get((element as PMetaVariable).name) + val copies = binding.map[ASTNode.copySubtree(ast, it)] + methodBodyList.addAll(copies) + } else if (element instanceof PTargetExpression) { + val binding = bindings.get("target") + val copies = binding.map[ASTNode.copySubtree(ast, it)] + methodBodyList.addAll(copies) + } else if (element instanceof PVariableDeclaration) { + methodBodyList.add(element.doBuildVariableDeclarationStatement) + } else { + methodBodyList.add(element.doBuild) + } + } + + if (methodBodyList.size == 1 && methodBodyList.head instanceof Block) { + method.body = (methodBodyList.head as Block) + } else { + block.statements.addAll(methodBodyList) + method.body = block + } + method + } + + //variable declaration builder + def private doBuildVariableDeclarationStatement(PVariableDeclaration varDecl) { + val fragment = ast.newVariableDeclarationFragment + + //adding variable name + if(varDecl.metaName !== null) { + fragment.name.identifier = nameBindings.get((varDecl.metaName as PMetaVariable).name) + } else { + fragment.name.identifier = varDecl.name + } + + val newVar = ast.newVariableDeclarationStatement(fragment) + + //adding variable type + if(varDecl.type !== null) { + newVar.type = Utils.getTypeFromId(typeReferenceQueue.remove, ast) + } else { + val name = (varDecl.metaType as PMetaVariable).name + newVar.type = ASTNode.copySubtree(ast, typeBindings.get(name) as ASTNode) as Type + } + newVar + } + + + //field declaration builder + def private dispatch doBuild(PVariableDeclaration varDecl) { + val fragment = ast.newVariableDeclarationFragment + + //adding field name + if(varDecl.metaName !== null) { + fragment.name.identifier = nameBindings.get( (varDecl.metaName as PMetaVariable).name ) + } else { + fragment.name.identifier = varDecl.name + } + + val newField = ast.newFieldDeclaration(fragment) + + //adding field visibility + if (varDecl.metaVisibility !== null) { + val metaVarName = (varDecl.metaVisibility as PMetaVariable).name + switch visibilityBindings.get(metaVarName) { + case PUBLIC: newField.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD)) + case PRIVATE: newField.modifiers().add(ast.newModifier(ModifierKeyword.PRIVATE_KEYWORD)) + case PROTECTED: newField.modifiers().add(ast.newModifier(ModifierKeyword.PROTECTED_KEYWORD)) + default: {} + } + } else { + switch varDecl.visibility { + case PUBLIC: newField.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD)) + case PRIVATE: newField.modifiers().add(ast.newModifier(ModifierKeyword.PRIVATE_KEYWORD)) + case PROTECTED: newField.modifiers().add(ast.newModifier(ModifierKeyword.PROTECTED_KEYWORD)) + default: {} + } + } + + //adding field type + if(varDecl.type !== null) { + newField.type = Utils.getTypeFromId(typeReferenceQueue.remove, ast) + } else { + val name = (varDecl.metaType as PMetaVariable).name + newField.type = ASTNode.copySubtree(ast, typeBindings.get(name)) as Type + } + + newField + } + + //method invocation (with expression) builder + def private dispatch ASTNode doBuild(PMemberFeatureCall featureCall) { + val methodInv = ast.newMethodInvocation + + //adding method invocation name + if (featureCall.feature !== null) { + methodInv.name.identifier = featureCall.feature + } else { + val name = (featureCall.metaFeature as PMetaVariable).name + methodInv.name.identifier = nameBindings.get(name) + } + + //adding method invocation arguments + if(featureCall.memberCallArguments !== null) { + val argumentList = argumentBindings.get((featureCall.memberCallArguments as PMetaVariable).name) + for (argument : argumentList) { + val expression = ASTNode.copySubtree(ast, argument) + methodInv.arguments.add(expression) + } + } + + //adding method invocation expression + val buildInvocationExpression = featureCall.memberCallTarget.doBuild + methodInv.expression = buildInvocationExpression as Expression + + val statement = ast.newExpressionStatement(methodInv) + statement + } + + //method invocation (without expression) builder + // This function is intended for method invocation without expression. Not to be confused with field access. (Field access don't have parantheses at the end) + def private dispatch doBuild(PFeatureCall featureCall) { + val methodInv = ast.newMethodInvocation + + //adding method invocation name + methodInv.name.identifier = featureCall.feature + + //adding method arguments + if(featureCall.featureCallArguments !== null) { + val argumentList = argumentBindings.get((featureCall.featureCallArguments as PMetaVariable).name) + for (argument : argumentList) { + val expression = ASTNode.copySubtree(ast, argument) + methodInv.arguments.add(expression) + } + } + + val statement = ast.newExpressionStatement(methodInv) + statement + } + + //return statement builder + def private dispatch ASTNode doBuild(PReturnExpression returnExpr) { + val returnStatement = ast.newReturnStatement + + if(returnExpr.expression !== null && returnExpr.expression instanceof PMetaVariable) { + val expression = bindings.get((returnExpr.expression as PMetaVariable).name) + println(expression.head.class) + if (expression.head instanceof Expression) { + val copy = ASTNode.copySubtree(ast, expression.head) as Expression + returnStatement.expression = copy + } + } + returnStatement + } + + //block builder + def private dispatch doBuild(PBlockExpression blockPattern) { val block = ast.newBlock - val builtStatements = blockPattern.expressions.doBuildPatterns block.statements.addAll(builtStatements) - - return block + block } - - def private doBuildPatterns(List patterns) { + + def private List doBuildPatterns(List patterns) { patterns.map[doBuild].filterNull.toList } - } diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/PatternMatcher.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/PatternMatcher.xtend index 845f6b2..c44ad14 100644 --- a/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/PatternMatcher.xtend +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/PatternMatcher.xtend @@ -1,67 +1,555 @@ package hu.elte.refjava.api.patterns import hu.elte.refjava.lang.refJava.PBlockExpression +import hu.elte.refjava.lang.refJava.PConstructorCall import hu.elte.refjava.lang.refJava.PExpression +import hu.elte.refjava.lang.refJava.PMemberFeatureCall import hu.elte.refjava.lang.refJava.PMetaVariable +import hu.elte.refjava.lang.refJava.PMethodDeclaration +import hu.elte.refjava.lang.refJava.PTargetExpression +import hu.elte.refjava.lang.refJava.PVariableDeclaration import hu.elte.refjava.lang.refJava.Pattern +import hu.elte.refjava.lang.refJava.Visibility +import java.util.ArrayList import java.util.List import java.util.Map +import java.util.Queue import org.eclipse.jdt.core.dom.ASTNode import org.eclipse.jdt.core.dom.Block +import org.eclipse.jdt.core.dom.ClassInstanceCreation +import org.eclipse.jdt.core.dom.ExpressionStatement +import org.eclipse.jdt.core.dom.FieldDeclaration +import org.eclipse.jdt.core.dom.MethodDeclaration +import org.eclipse.jdt.core.dom.MethodInvocation +import org.eclipse.jdt.core.dom.Modifier +import org.eclipse.jdt.core.dom.SingleVariableDeclaration +import org.eclipse.jdt.core.dom.Type +import org.eclipse.jdt.core.dom.VariableDeclarationFragment +import org.eclipse.jdt.core.dom.VariableDeclarationStatement +import org.eclipse.xtext.EcoreUtil2 +import org.eclipse.jdt.core.dom.Expression +import hu.elte.refjava.lang.refJava.PFeatureCall +import hu.elte.refjava.lang.refJava.PReturnExpression +import org.eclipse.jdt.core.dom.ReturnStatement class PatternMatcher { - + + ArrayList modifiedTarget val Pattern pattern Map> bindings = newHashMap - + Map nameBindings + Map typeBindings + Map> parameterBindings + Map visibilityBindings + Map> argumentBindings + Queue typeReferenceQueue + new(Pattern pattern) { this.pattern = pattern } - + def getBindings() { bindings } + + def getModifiedTarget() { + modifiedTarget + } + + def match(Pattern targetPattern, List target, Map nameBindings, Map typeBindings, Map> parameterBindings, Map visibilityBindings, Map> argumentBindings, String typeRefString) { + bindings.clear + this.nameBindings = nameBindings + this.typeBindings = typeBindings + this.parameterBindings = parameterBindings + this.visibilityBindings = visibilityBindings + this.argumentBindings = argumentBindings + + if (typeRefString !== null) { + val tmp = typeRefString.split("\\|") + this.typeReferenceQueue = newLinkedList + this.typeReferenceQueue.addAll(tmp) + } + return doMatchChildren(targetPattern.patterns, target) + } - def match(List target) { + //this function gets called during the matching + def match(List target, Map nameBindings, Map typeBindings, Map> parameterBindings, Map visibilityBindings, Map> argumentBindings, String typeRefString) { + this.nameBindings = nameBindings + this.typeBindings = typeBindings + this.parameterBindings = parameterBindings + this.visibilityBindings = visibilityBindings + this.argumentBindings = argumentBindings + + if (typeRefString !== null) { + val tmp = typeRefString.split("\\|") + this.typeReferenceQueue = newLinkedList + this.typeReferenceQueue.addAll(tmp) + } + bindings.clear - return doMatchChildren(pattern.patterns, target) + modifiedTarget = newArrayList + modifiedTarget.addAll(target) + + val patterns = pattern.patterns + val isTargetExists = EcoreUtil2.getAllContentsOfType(pattern, PTargetExpression).size > 0 + if (!isTargetExists) { + doMatchChildren(patterns, target) + } else { + doMatchChildrenWithTarget(patterns, target) + } } + /////////////////////// + // doMatch overloads // + /////////////////////// def private dispatch doMatch(PMetaVariable metaVar, ASTNode anyNode) { bindings.put(metaVar.name, #[anyNode]) - return true + true } - + + def private dispatch doMatch(PMetaVariable multiMetavar, List nodes) { + if(multiMetavar.multi) { + bindings.put(multiMetavar.name, nodes) + true + } + } + def private dispatch boolean doMatch(PBlockExpression blockPattern, Block block) { doMatchChildren(blockPattern.expressions, block.statements) } + + //constructor call matching + def private dispatch boolean doMatch(PConstructorCall constCall, ClassInstanceCreation classInstance) { + + //matching constructor call name + var boolean nameCheck + if (constCall.metaName !== null) { + val name = nameBindings.get((constCall.metaName as PMetaVariable).name) + if (name === null) { + val className = classInstance.type.toString + nameBindings.put((constCall.metaName as PMetaVariable).name, className) + nameCheck = true + } else { + nameCheck = name == classInstance.type.toString + } + } else { + nameCheck = constCall.name == classInstance.type.toString + } + + //matching constructor call's methods + var boolean anonClassCheck + if (classInstance.anonymousClassDeclaration !== null && constCall.elements !== null) { + anonClassCheck = doMatchChildren(constCall.elements, classInstance.anonymousClassDeclaration.bodyDeclarations) + } else { + anonClassCheck = true + } + + return nameCheck && anonClassCheck + } + + //method matching + def private dispatch boolean doMatch(PMethodDeclaration pMethodDecl, MethodDeclaration methodDecl) { + + //matching method name + var boolean nameCheck + if(pMethodDecl.prefix.metaName !== null) { + val name = nameBindings.get((pMethodDecl.prefix.metaName as PMetaVariable).name) + if (name === null) { + val methodName = methodDecl.name.identifier + nameBindings.put((pMethodDecl.prefix.metaName as PMetaVariable).name, methodName) + nameCheck = true + } else { + nameCheck = name == methodDecl.name.identifier + } + } else { + nameCheck = pMethodDecl.prefix.name == methodDecl.name.identifier + } + + //matching method visibility + var boolean visibilityCheck + val modifiers = methodDecl.getModifiers + if (pMethodDecl.prefix.metaVisibility !== null) { + val metaVarName = (pMethodDecl.prefix.metaVisibility as PMetaVariable).name + if (visibilityBindings.get(metaVarName) === null) { + switch modifiers { + case Modifier.isPublic(modifiers) : visibilityBindings.put(metaVarName, Visibility.PUBLIC) + case Modifier.isPrivate(modifiers) : visibilityBindings.put(metaVarName, Visibility.PRIVATE) + case Modifier.isProtected(modifiers) : visibilityBindings.put(metaVarName, Visibility.PROTECTED) + default : visibilityBindings.put(metaVarName, Visibility.PACKAGE) + } + visibilityCheck = true + } else { + switch visibilityBindings.get(metaVarName) { + case PUBLIC: visibilityCheck = Modifier.isPublic(modifiers) + case PRIVATE: visibilityCheck = Modifier.isPrivate(modifiers) + case PROTECTED: visibilityCheck = Modifier.isProtected(modifiers) + default: visibilityCheck = modifiers.bitwiseAnd(Modifier.PROTECTED) == 0 && modifiers.bitwiseAnd(Modifier.PRIVATE) == 0 && modifiers.bitwiseAnd(Modifier.PUBLIC) == 0 + } + } + } else { + switch pMethodDecl.prefix.visibility { + case PUBLIC: visibilityCheck = Modifier.isPublic(modifiers) + case PRIVATE: visibilityCheck = Modifier.isPrivate(modifiers) + case PROTECTED: visibilityCheck = Modifier.isProtected(modifiers) + default: visibilityCheck = modifiers.bitwiseAnd(Modifier.PROTECTED) == 0 && modifiers.bitwiseAnd(Modifier.PRIVATE) == 0 && modifiers.bitwiseAnd(Modifier.PUBLIC) == 0 + } + } + + //matching method return value + var boolean returnCheck + if(pMethodDecl.prefix.metaType !== null) { + val type = typeBindings.get((pMethodDecl.prefix.metaType as PMetaVariable).name) + if (type === null) { + val returnType = methodDecl.returnType2 + typeBindings.put((pMethodDecl.prefix.metaType as PMetaVariable).name, returnType) + returnCheck = true + } else { + returnCheck = type.resolveBinding.qualifiedName == methodDecl.returnType2.resolveBinding.qualifiedName + } + } else { + returnCheck = methodDecl.returnType2.resolveBinding.qualifiedName == typeReferenceQueue.remove + } + + //matching method parameters + var boolean parameterCheck = true + if (pMethodDecl.parameters.size > 0) { + if (pMethodDecl.parameters.size != methodDecl.parameters.size) { + parameterCheck = false + } else { + val argIt = pMethodDecl.parameters.iterator + val paramIt = (methodDecl.parameters as List).iterator + while(argIt.hasNext && parameterCheck) { + val arg = argIt.next + val param = paramIt.next + parameterCheck = param.name.identifier == arg.name && param.type.resolveBinding.qualifiedName == typeReferenceQueue.remove + } + } + } else if (pMethodDecl.metaParameters !== null) { + val metaVar = pMethodDecl.metaParameters as PMetaVariable + val parameters = parameterBindings.get((pMethodDecl.metaParameters as PMetaVariable).name) + if (parameters === null) { + parameterBindings.put(metaVar.name, methodDecl.parameters) + parameterCheck = true + } else { + if (parameters.size != methodDecl.parameters.size) { + parameterCheck = false + } else { + val it1 = parameters.iterator + val it2 = (methodDecl.parameters as List).iterator + while(it1.hasNext && parameterCheck) { + val param1 = it1.next + val param2 = it2.next + parameterCheck = param1.name.identifier == param2.name.identifier && param1.type.resolveBinding.qualifiedName == param2.type.resolveBinding.qualifiedName + } + } + } + } else { + parameterCheck = methodDecl.parameters.size == 0 + } + + //matching method body + val boolean bodyCheck = doMatch(pMethodDecl.body, methodDecl.body) + + return nameCheck && visibilityCheck && parameterCheck && returnCheck && bodyCheck + } + + //method invocation matching (with expression) + def private dispatch boolean doMatch(PMemberFeatureCall featureCall, ExpressionStatement expStatement) { + if (expStatement.expression instanceof MethodInvocation) { + val methodInv = expStatement.expression as MethodInvocation + + //matching method invocation name + var boolean nameCheck + if (featureCall.feature !== null) { + nameCheck = featureCall.feature == methodInv.name.identifier + } else { + val name = nameBindings.get((featureCall.metaFeature as PMetaVariable).name) + if (name === null) { + val methodName = methodInv.name.identifier + nameBindings.put((featureCall.metaFeature as PMetaVariable).name, methodName) + nameCheck = true + } else { + nameCheck = name == methodInv.name.identifier + } + } + + //matching method invocation parameters + var boolean argumentCheck = true + if(featureCall.memberCallArguments !== null) { + val metaVarName = (featureCall.memberCallArguments as PMetaVariable).name + if (argumentBindings.get(metaVarName) === null) { + argumentBindings.put(metaVarName, methodInv.arguments) + argumentCheck = true + } else { + val arguments = argumentBindings.get(metaVarName) + if(methodInv.arguments.size != arguments.size) { + argumentCheck = false + } else { + //currently we can't assign values to argument-binding meta variables + argumentCheck = true + } + } + } else { + argumentCheck = methodInv.arguments.size == 0 + } + + //matching method invocation expression + val boolean expressionCheck = doMatch(featureCall.memberCallTarget, methodInv.expression) + + return nameCheck && argumentCheck && expressionCheck + } else { + return false + } + } + + + //method invocation matching (without expression) + def private dispatch boolean doMatch(PFeatureCall featureCall, ExpressionStatement expStatement) { + if (expStatement.expression instanceof MethodInvocation) { + val methodInv = expStatement.expression as MethodInvocation + + //matching method invocation name + var boolean nameCheck + if (featureCall.feature !== null) { + nameCheck = featureCall.feature == methodInv.name.identifier + } else { + val name = nameBindings.get((featureCall.metaFeature as PMetaVariable).name) + if (name === null) { + val methodName = methodInv.name.identifier + nameBindings.put((featureCall.metaFeature as PMetaVariable).name, methodName) + nameCheck = true + } else { + nameCheck = name == methodInv.name.identifier + } + } + + //matching method invocation parameters + var boolean argumentCheck = true + if(featureCall.featureCallArguments !== null) { + val metaVarName = (featureCall.featureCallArguments as PMetaVariable).name + if (argumentBindings.get(metaVarName) === null) { + argumentBindings.put(metaVarName, methodInv.arguments) + argumentCheck = true + } else { + val arguments = argumentBindings.get(metaVarName) + if(methodInv.arguments.size != arguments.size) { + argumentCheck = false + } else { + //currently we can't assign values to argument-binding meta variables + argumentCheck = true + } + } + } else { + argumentCheck = methodInv.arguments.size == 0 + } + + return nameCheck && argumentCheck + } else { + return false + } + } + + //variable declaration matching + def private dispatch boolean doMatch(PVariableDeclaration varDecl, VariableDeclarationStatement varDeclStatement) { + + //matching variable declaration name + var boolean nameCheck + if (varDecl.metaName !== null) { + val name = nameBindings.get((varDecl.metaName as PMetaVariable).name) + if(name === null) { + val varName = (varDeclStatement.fragments.head as VariableDeclarationFragment).name.identifier + nameBindings.put((varDecl.metaName as PMetaVariable).name, varName) + nameCheck = true + } else { + nameCheck = name == (varDeclStatement.fragments.head as VariableDeclarationFragment).name.identifier + } + } else { + nameCheck = varDecl.name == (varDeclStatement.fragments.head as VariableDeclarationFragment).name.identifier + } + + //matching variable declaration type + var boolean typeCheck + if(varDecl.type !== null) { + typeCheck = varDeclStatement.type.resolveBinding.qualifiedName == typeReferenceQueue.remove + } else { + val type = typeBindings.get((varDecl.metaType as PMetaVariable).name) + if (type === null) { + val varType = varDeclStatement.type + typeBindings.put((varDecl.metaType as PMetaVariable).name, varType) + typeCheck = true + } else { + typeCheck = type.resolveBinding.qualifiedName == varDeclStatement.type.resolveBinding.qualifiedName + } + } + + return nameCheck && typeCheck + } + + //field declaration matching + def private dispatch boolean doMatch(PVariableDeclaration varDecl, FieldDeclaration fieldDecl) { + + //matching field declaration name + var boolean nameCheck + if (varDecl.metaName !== null) { + val name = nameBindings.get((varDecl.metaName as PMetaVariable).name) + if (name === null) { + val fieldName = (fieldDecl.fragments.head as VariableDeclarationFragment).name.identifier + nameBindings.put((varDecl.metaName as PMetaVariable).name, fieldName) + nameCheck = true + } else { + nameCheck = name == (fieldDecl.fragments.head as VariableDeclarationFragment).name.identifier + } + } else { + nameCheck = varDecl.name == (fieldDecl.fragments.head as VariableDeclarationFragment).name.identifier + } + + //matching field declaration visibility + var boolean visibilityCheck + val modifiers = fieldDecl.getModifiers + if (varDecl.metaVisibility !== null) { + val metaVarName = (varDecl.metaVisibility as PMetaVariable).name + if (visibilityBindings.get(metaVarName) === null) { + switch modifiers { + case Modifier.isPublic(modifiers) : visibilityBindings.put(metaVarName, Visibility.PUBLIC) + case Modifier.isPrivate(modifiers) : visibilityBindings.put(metaVarName, Visibility.PRIVATE) + case Modifier.isProtected(modifiers) : visibilityBindings.put(metaVarName, Visibility.PROTECTED) + default : visibilityBindings.put(metaVarName, Visibility.PACKAGE) + } + visibilityCheck = true + } else { + switch visibilityBindings.get(metaVarName) { + case PUBLIC: visibilityCheck = Modifier.isPublic(modifiers) + case PRIVATE: visibilityCheck = Modifier.isPrivate(modifiers) + case PROTECTED: visibilityCheck = Modifier.isProtected(modifiers) + default: visibilityCheck = modifiers.bitwiseAnd(Modifier.PROTECTED) == 0 && modifiers.bitwiseAnd(Modifier.PRIVATE) == 0 && modifiers.bitwiseAnd(Modifier.PUBLIC) == 0 + } + } + } else { + switch varDecl.visibility { + case PUBLIC: visibilityCheck = Modifier.isPublic(modifiers) + case PRIVATE: visibilityCheck = Modifier.isPrivate(modifiers) + case PROTECTED: visibilityCheck = Modifier.isProtected(modifiers) + default: visibilityCheck = modifiers.bitwiseAnd(Modifier.PROTECTED) == 0 && modifiers.bitwiseAnd(Modifier.PRIVATE) == 0 && modifiers.bitwiseAnd(Modifier.PUBLIC) == 0 + } + } + + //matching field declaration type + var boolean typeCheck + if(varDecl.type !== null) { + typeCheck = fieldDecl.type.resolveBinding.qualifiedName == typeReferenceQueue.remove + } else { + val type = typeBindings.get((varDecl.metaType as PMetaVariable).name) + if (type === null) { + val fieldType = fieldDecl.type + typeBindings.put((varDecl.metaType as PMetaVariable).name, fieldType) + typeCheck = true + } else { + typeCheck = type.resolveBinding.qualifiedName == fieldDecl.type.resolveBinding.qualifiedName + } + } + return nameCheck && visibilityCheck && typeCheck + } + + def private dispatch boolean doMatch(PReturnExpression returnExpr, ReturnStatement returnStatement) { + + var boolean expressionCheck + if (returnExpr.expression !== null) { + val exp = returnExpr.expression + if(exp instanceof PMetaVariable) { + bindings.put(exp.name, #[returnStatement.expression]) + expressionCheck = true + } else { + expressionCheck = false + } + } else { + expressionCheck = true + } + expressionCheck + } + + def private dispatch doMatch(PExpression anyOtherPattern, ASTNode anyOtherNode) { false } - + + /////////////////////// + // children matching // + /////////////////////// def private doMatchChildren(List patterns, List nodes) { - if (patterns.size == 1 && patterns.head instanceof PMetaVariable) { - val metaVar = patterns.head as PMetaVariable - if (metaVar.isMulti) { - bindings.put(metaVar.name, nodes) - return true - } + if (patterns.size == 1 && patterns.head instanceof PMetaVariable && (patterns.head as PMetaVariable).multi) { + bindings.put("target", nodes) + bindings.put((patterns.head as PMetaVariable).name , nodes) + return true } - - if (patterns.size != nodes.size) { + + if (!patterns.exists[it instanceof PMetaVariable && (it as PMetaVariable).multi] && nodes.size != patterns.size) { return false } - - val pIt = patterns.iterator + val nIt = nodes.iterator - while (pIt.hasNext) { - if (!doMatch(pIt.next, nIt.next)) { - return false + for (var int i = 0; i < patterns.size; i++) { + if( !(patterns.get(i) instanceof PMetaVariable) || !(patterns.get(i) as PMetaVariable).multi ) { + if (!doMatch(patterns.get(i), nIt.next)) { + return false + } + } else { + val preMultiMetavar = patterns.take(i).size + val postMultiMetavar = patterns.drop(i + 1).size + var List matchingNodes = newArrayList + var int j = 0 + + while (j != nodes.size - (preMultiMetavar + postMultiMetavar) ) { + matchingNodes.add(nIt.next) + j++ + } + + if(!doMatch(patterns.get(i), matchingNodes)) { + return false + } } } - - return true + bindings.put("target", nodes) + true } + + def private doMatchChildrenWithTarget(List patterns, List selectedNodes) { + if (patterns.size == 1 && patterns.head instanceof PTargetExpression) { + bindings.put("target", selectedNodes) + return true + } + + var List preTargetExpression = patterns.clone.takeWhile[ !(it instanceof PTargetExpression) ].toList + var List postTargetExpression = patterns.clone.reverse.takeWhile[ !(it instanceof PTargetExpression) ].toList.reverse + + val List targetEnvironment = newArrayList + // If the user use the program as we intended, the selectedNodes.head.parent will be direct contents of a block. + targetEnvironment.addAll( (selectedNodes.head.parent as Block).statements ) + + var List preSelectedNodes = (targetEnvironment as List).clone.takeWhile[ it != selectedNodes.head ].toList + var List postSelectedNodes = (targetEnvironment as List).clone.reverse.takeWhile[ it != selectedNodes.last ].toList.reverse + + var boolean pre + var boolean post + if (!preTargetExpression.exists[ it instanceof PMetaVariable && (it as PMetaVariable).isMulti] ) { + val preSelectedNodesToMatch = preSelectedNodes.clone.reverse.take(preTargetExpression.size).toList.reverse + pre = doMatchChildren(preTargetExpression, preSelectedNodesToMatch) + modifiedTarget.addAll(0, preSelectedNodesToMatch) + } else { + pre = doMatchChildren(preTargetExpression, preSelectedNodes) + modifiedTarget.addAll(0, preSelectedNodes) + } + + if (!postTargetExpression.exists[ it instanceof PMetaVariable && (it as PMetaVariable).isMulti] ) { + val postSelectedNodesToMatch = postSelectedNodes.clone.take(postTargetExpression.size).toList + post = doMatchChildren(postTargetExpression, postSelectedNodesToMatch) + modifiedTarget.addAll(postSelectedNodesToMatch) + } else { + post = doMatchChildren(postTargetExpression, postSelectedNodes) + modifiedTarget.addAll(postSelectedNodes) + } + bindings.put("target", selectedNodes) + return pre && post + } } diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/PatternParser.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/PatternParser.xtend index c77d89b..b7cd406 100644 --- a/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/PatternParser.xtend +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/PatternParser.xtend @@ -10,11 +10,10 @@ import org.eclipse.xtext.resource.XtextResource import org.eclipse.xtext.resource.XtextResourceSet class PatternParser { - @Inject static XtextResourceSet resourceSet static Resource resource static boolean initialized = false - + def static parse(String patternString) { if (!initialized) { resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL, Boolean.TRUE) @@ -25,15 +24,13 @@ class PatternParser { if (resource.loaded) { resource.unload } - + val paddedPatternString = '''package p; local refactoring l() «patternString» ~ nothing''' val inputStream = new ByteArrayInputStream(paddedPatternString.bytes) - resource.load(inputStream, resourceSet.loadOptions) - + resource.load(inputStream, resourceSet.loadOptions) val file = resource.contents.head as File val refact = file.refactorings.head as SchemeInstanceRule - + return refact.matchingPattern - } - + } } diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/Utils.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/Utils.xtend new file mode 100644 index 0000000..6ea8601 --- /dev/null +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/api/patterns/Utils.xtend @@ -0,0 +1,151 @@ +package hu.elte.refjava.api.patterns + +import hu.elte.refjava.lang.refJava.Pattern +import org.eclipse.jdt.core.dom.AST +import org.eclipse.jdt.core.dom.ASTNode +import org.eclipse.jdt.core.dom.CompilationUnit +import org.eclipse.jdt.core.dom.FieldDeclaration +import org.eclipse.jdt.core.dom.MethodDeclaration +import org.eclipse.jdt.core.dom.PrimitiveType +import org.eclipse.jdt.core.dom.TypeDeclaration +import org.eclipse.jdt.core.dom.VariableDeclarationStatement +import org.eclipse.xtext.EcoreUtil2 +import org.eclipse.xtext.common.types.JvmTypeReference +import org.eclipse.jdt.core.dom.ASTParser +import org.eclipse.jdt.core.ICompilationUnit +import org.eclipse.jface.text.IDocument +import org.eclipse.jdt.core.dom.Assignment +import org.eclipse.jdt.core.dom.Block +import hu.elte.refjava.lang.refJava.PExpression +import hu.elte.refjava.lang.refJava.PMemberFeatureCall +import hu.elte.refjava.lang.refJava.PConstructorCall +import hu.elte.refjava.lang.refJava.PMethodDeclaration +import hu.elte.refjava.lang.refJava.PVariableDeclaration + +class Utils { + + def static getTypeFromId(String id, AST ast) { + + val primitiveTypes = newArrayList("byte", "short", "char", "int", "long", "float", "double", "boolean", "void") + + if(primitiveTypes.contains(id)) { + //primitive type + return ast.newPrimitiveType(PrimitiveType.toCode(id)) + } else if (id.contains("[") && id.contains("]")) { + //array type + val char openingSymbol = '[' + val char closingSymbol = ']' + var String type = "" + var int dimension = 0 + for(var int i = 0; i < id.length; i++) { + if( id.charAt(i).identityEquals(openingSymbol) ) { + dimension++ + } else if ( !id.charAt(i).identityEquals(openingSymbol) && !id.charAt(i).identityEquals(closingSymbol)) { + type += id.charAt(i) + } + } + if(primitiveTypes.contains(type)) { + //primitive array type + return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.toCode(type)), dimension) + } else { + //simple array type + val simpleName = type.split("\\.") + return ast.newArrayType(ast.newSimpleType(ast.newSimpleName(simpleName.last)), dimension) + } + } else { + //simple type + val simpleName = id.split("\\.").last + return ast.newSimpleType(ast.newSimpleName(simpleName)) + } + } + + def static getTypeReferenceString(Pattern pattern) { + var String typeReferenceString = "" + val types = EcoreUtil2.getAllContentsOfType(pattern, JvmTypeReference) + for(type : types) { + typeReferenceString = typeReferenceString + type.identifier + "|" + } + return typeReferenceString + } + + def static getTypeDeclaration(ASTNode node) { + var tmp = node + while (!(tmp instanceof TypeDeclaration)) { + tmp = tmp.parent + } + tmp as TypeDeclaration + } + + def static getCompilationUnit(ASTNode node) { + var tmp = node + while (!(tmp instanceof CompilationUnit)) { + tmp = tmp.parent + } + tmp as CompilationUnit + } + + def static getICompilationUnit(ASTNode node) { + node.compilationUnit.getJavaElement as ICompilationUnit + } + + def static getMethodDeclaration(ASTNode node) { + var tmp = node + while (!(tmp instanceof MethodDeclaration) && tmp !== null) { + tmp = tmp.parent + } + tmp as MethodDeclaration + } + + def static getBlock(ASTNode node) { + var tmp = node + while (!(tmp instanceof Block) && tmp !== null) { + tmp = tmp.parent + } + tmp as Block + } + + def static getFieldDeclaration(ASTNode node) { + var tmp = node + while (!(tmp instanceof FieldDeclaration) && tmp !== null) { + tmp = tmp.parent + } + tmp as FieldDeclaration + } + + def static getVariableDeclaration(ASTNode node) { + var tmp = node + while (!(tmp instanceof VariableDeclarationStatement) && tmp !== null) { + tmp = tmp.parent + } + tmp as VariableDeclarationStatement + } + + def static getAssignment(ASTNode node) { + var tmp = node + while (!(tmp instanceof Assignment) && tmp !== null) { + tmp = tmp.parent + } + tmp as Assignment + } + + def static parseSourceCode(ICompilationUnit iCompUnit) { + val parser = ASTParser.newParser(AST.JLS12); + parser.resolveBindings = true + parser.source = iCompUnit + parser.createAST(null) as CompilationUnit + } + + def static applyChanges(CompilationUnit compUnit, IDocument document) { + compUnit.rewrite(document, null).apply(document) + } + + def static isValidLambdaExpression (PExpression expression) { + (expression instanceof PMemberFeatureCall) + && ((expression as PMemberFeatureCall).memberCallTarget instanceof PConstructorCall) + && (((expression as PMemberFeatureCall).memberCallTarget as PConstructorCall).anonInstance) + && ((expression as PMemberFeatureCall).memberCallTarget as PConstructorCall).elements.exists[it instanceof PMethodDeclaration] + && ((expression as PMemberFeatureCall).memberCallTarget as PConstructorCall).elements.forall[it instanceof PMethodDeclaration || it instanceof PVariableDeclaration] + } + + +} \ No newline at end of file diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/lang/GenerateRefJava.mwe2 b/hu.elte.refjava.lang/src/hu/elte/refjava/lang/GenerateRefJava.mwe2 index d3decd5..2be55a9 100644 --- a/hu.elte.refjava.lang/src/hu/elte/refjava/lang/GenerateRefJava.mwe2 +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/lang/GenerateRefJava.mwe2 @@ -11,11 +11,15 @@ Workflow { project = StandardProjectConfig { baseName = "hu.elte.refjava.lang" rootPath = rootPath - + + runtimeTest = { + enabled = true + } + eclipsePlugin = { enabled = true } - + createEclipseMetaData = true } @@ -36,6 +40,10 @@ Workflow { validator = { generateDeprecationValidation = true } + + junitSupport = { + junitVersion = "5" + } } } } diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/lang/RefJava.xtext b/hu.elte.refjava.lang/src/hu/elte/refjava/lang/RefJava.xtext index 190266a..1e7ff80 100644 --- a/hu.elte.refjava.lang/src/hu/elte/refjava/lang/RefJava.xtext +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/lang/RefJava.xtext @@ -31,9 +31,22 @@ SchemeInstanceRule: ('in' definitionLocation=InDefinitionLocation)? definitionPattern=Pattern)? ('when' - precondition=XExpression)? + ('assignment' + assignments=AssignmentList)? + ('precondition' + precondition=XExpression)? + )? +; + +AssignmentList: + assignment+=MetaVariableAssignment =>(';' assignment+=MetaVariableAssignment)* ';'? +; + +MetaVariableAssignment: + {MetaVariableAssignment} metaVariable=PMetaVariable '=' value=XExpression ; + enum SchemeType: LOCAL='local' | BLOCK='block' | LAMBDA='lambda' | CLASS='class' ; @@ -55,8 +68,12 @@ CompositeRefactoringRule: // Patterns // ////////////// +enum MetaVariableType: + CODE='#' | NAME='name#' | TYPE='type#' | PARAMETER='parameter#' | VISIBILITY='visibility#' | ARGUMENT='argument#' +; + PMetaVariable returns PExpression: - {PMetaVariable} '#' name=ID multi?='..'? + {PMetaVariable} type=MetaVariableType name=ID multi?='..'? ; Pattern: @@ -72,8 +89,8 @@ PExpressionOrVarDeclaration returns PExpression: PDeclaration returns PExpression: PVariableDeclaration ( {PMethodDeclaration.prefix=current} '(' ( - arguments+=FullJvmFormalParameter (',' arguments+=FullJvmFormalParameter)* | metaArguments=PMetaVariable - )? ')' (body=PBlockExpression | metaBody=PMetaVariable) + parameters+=FullJvmFormalParameter (',' parameters+=FullJvmFormalParameter)* | metaParameters=PMetaVariable + )? ')' (body=PBlockExpression) )? ; @@ -81,7 +98,7 @@ PVariableDeclaration returns PVariableDeclaration: =>({PVariableDeclaration} ( // =>: global, see PExpressionOrVarDeclaration->...->PFeatureCall (visibility=Visibility | metaVisibility=PMetaVariable)? // for methods (type=JvmTypeReference | metaType=PMetaVariable) - (name=ValidID | metaName=PMetaVariable) + (name=ValidID | metaName=PMetaVariable) )) ('=' right=PExpression)? ; @@ -93,11 +110,12 @@ PExpression returns PExpression: PMemberFeatureCall ; + PMemberFeatureCall returns PExpression: PPrimaryExpression ( =>({PMemberFeatureCall.memberCallTarget=current} ".") (feature=IdOrSuper | metaFeature=PMetaVariable) ( - =>'(' (memberCallArguments+=PExpression (',' memberCallArguments+=PExpression)*)? ')' + =>'(' memberCallArguments=PMetaVariable? ')' //currently restricted to PMetaVariable, should be PExpression )? )* ; @@ -114,8 +132,8 @@ PPrimaryExpression returns PExpression ; PConstructorCall returns PExpression: - {PConstructorCall} 'new' constructor=[types::JvmConstructor|QualifiedName] - =>'(' (arguments+=PExpression (',' arguments+=PExpression)*)? ')' ( + {PConstructorCall} 'new' (name=ValidID | metaName=PMetaVariable) + =>'(' arguments=PMetaVariable? ')' ( //currently restricted to PMetaVariable, should be PExpression anonInstance?='{' (elements+=PDeclaration =>(';' elements+=PDeclaration)* ';'?)? '}' // =>: local, distinguishes semicolons )? @@ -130,17 +148,17 @@ PBlockExpression returns PExpression: PFeatureCall returns PExpression: {PFeatureCall} ( - feature=IdOrSuper ( - =>'(' (featureCallArguments+=PExpression (',' featureCallArguments+=PExpression)*)? ')' + feature=IdOrSuper ( + =>'(' featureCallArguments=PMetaVariable? ')' //currently restricted to PMetaVariable, should be PExpression )? ) | ( - metaFeature=PMetaVariable - =>'(' (featureCallArguments+=PExpression (',' featureCallArguments+=PExpression)*)? ')' + metaFeature=PMetaVariable + =>'(' featureCallArguments=PMetaVariable? ')' //currently restricted to PMetaVariable, should be PExpression ) ; PReturnExpression returns PExpression: - {PReturnExpression} 'return' (->expression=PExpression)? + {PReturnExpression} 'return' (->expression=PMetaVariable)? ; PTargetExpression returns PExpression: @@ -174,10 +192,15 @@ XPrimaryExpression returns xbase::XExpression | XTryCatchFinallyExpression | XParenthesizedExpression | MetaVariable + | TargetExpression ; MetaVariable returns xbase::XExpression: - {MetaVariable} '#' name=ID multi?='..'? + {MetaVariable} type=MetaVariableType name=ID multi?='..'? +; + +TargetExpression returns xbase::XExpression: + {TargetExpression} 'target' ; @Override diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/lang/compiler/RefJavaCompiler.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/lang/compiler/RefJavaCompiler.xtend index 768ce37..1599511 100644 --- a/hu.elte.refjava.lang/src/hu/elte/refjava/lang/compiler/RefJavaCompiler.xtend +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/lang/compiler/RefJavaCompiler.xtend @@ -1,6 +1,8 @@ package hu.elte.refjava.lang.compiler import hu.elte.refjava.lang.refJava.MetaVariable +import hu.elte.refjava.lang.refJava.MetaVariableType +import hu.elte.refjava.lang.refJava.TargetExpression import org.eclipse.xtext.xbase.XExpression import org.eclipse.xtext.xbase.compiler.XbaseCompiler import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable @@ -11,6 +13,8 @@ class RefJavaCompiler extends XbaseCompiler { switch expression { MetaVariable: expression.toJavaStatement(builder) + TargetExpression: + expression.toJavaStatement(builder) default: super.doInternalToJavaStatement(expression, builder, isReferenced) } @@ -19,21 +23,40 @@ class RefJavaCompiler extends XbaseCompiler { def void toJavaStatement(MetaVariable metaVar, ITreeAppendable it) { // do nothing, a metavariable is strictly an expression } + + def void toJavaStatement(TargetExpression targetExpr, ITreeAppendable it) { + // do nothing, a metavariable is strictly an expression + } override protected internalToConvertedExpression(XExpression expression, ITreeAppendable builder) { switch expression { MetaVariable: expression.toJavaExpression(builder) + TargetExpression: + expression.toJavaExpression(builder) default: super.internalToConvertedExpression(expression, builder) } } def dispatch void toJavaExpression(MetaVariable metaVar, ITreeAppendable it) { - append('''bindings.get("«metaVar.name»")''') - if (!metaVar.multi) { - append(".get(0)") + if(metaVar.type == MetaVariableType.CODE) { + append('''bindings.get("«metaVar.name»")''') + if (!metaVar.multi) { + append(".get(0)") + } + } else if (metaVar.type == MetaVariableType.NAME) { + append('''nameBindings.get("«metaVar.name»")''') + } else if (metaVar.type == MetaVariableType.TYPE) { + append('''typeBindings.get("«metaVar.name»")''') + } else if (metaVar.type == MetaVariableType.PARAMETER) { + append('''parameterBindings.get("«metaVar.name»")''') + } else if (metaVar.type == MetaVariableType.VISIBILITY) { + append('''visibilityBindings.get("«metaVar.name»")''') } } - + + def dispatch void toJavaExpression(TargetExpression targetExpr, ITreeAppendable it) { + append('''bindings.get("target")''') + } } diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/lang/jvmmodel/RefJavaJvmModelInferrer.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/lang/jvmmodel/RefJavaJvmModelInferrer.xtend index 4aec72c..2d92418 100644 --- a/hu.elte.refjava.lang/src/hu/elte/refjava/lang/jvmmodel/RefJavaJvmModelInferrer.xtend +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/lang/jvmmodel/RefJavaJvmModelInferrer.xtend @@ -1,9 +1,18 @@ package hu.elte.refjava.lang.jvmmodel import com.google.inject.Inject +import hu.elte.refjava.api.BlockRefactoring +import hu.elte.refjava.api.ClassRefactoring +import hu.elte.refjava.api.LambdaRefactoring import hu.elte.refjava.api.LocalRefactoring +import hu.elte.refjava.api.patterns.Utils +import hu.elte.refjava.lang.refJava.MetaVariableType +import hu.elte.refjava.lang.refJava.PMetaVariable +import hu.elte.refjava.lang.refJava.Pattern import hu.elte.refjava.lang.refJava.SchemeInstanceRule import hu.elte.refjava.lang.refJava.SchemeType +import java.util.List +import org.eclipse.jdt.core.dom.Type import org.eclipse.xtext.common.types.JvmVisibility import org.eclipse.xtext.naming.IQualifiedNameProvider import org.eclipse.xtext.serializer.ISerializer @@ -16,15 +25,106 @@ class RefJavaJvmModelInferrer extends AbstractModelInferrer { @Inject extension IQualifiedNameProvider @Inject extension ISerializer @Inject extension JvmTypesBuilder - + def dispatch infer(SchemeInstanceRule rule, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) { acceptor.accept(rule.toClass(rule.fullyQualifiedName)) [ superTypes += rule.type.toSuperType.typeRef - + + //type parsing doesn't work.. + val typeRefGetter = [ Pattern pattern | + if (pattern !== null) { + Utils.getTypeReferenceString(pattern) + } else { + "" + } + ] + + val matchingTypeReferenceString = typeRefGetter.apply(rule.matchingPattern) + val replacementTypeReferenceString = typeRefGetter.apply(rule.replacementPattern) + val targetTypeReferenceString = typeRefGetter.apply(rule.targetPattern) + val definitionTypeReferenceString = typeRefGetter.apply(rule.definitionPattern) + members += rule.toConstructor [ body = '''super("«rule.matchingPattern.serialize.trim»", "«rule.replacementPattern.serialize.trim»");''' ] - + + val endl = System.getProperty("line.separator"); + var String callings = "" + if(rule.assignments !== null) { + for (assignment : rule.assignments.assignment) { + val metaVar = assignment.metaVariable as PMetaVariable + val metaVarName = (assignment.metaVariable as PMetaVariable).name + + if (metaVar.type == MetaVariableType.NAME) { + members += rule.toMethod("valueof_name_" + metaVarName, typeof(String).typeRef) [ + visibility = JvmVisibility.PRIVATE + body = assignment.value + ] + callings = callings + "set_name_" + metaVarName + "();" + endl + members += rule.toMethod("set_name_" + metaVarName, typeof(void).typeRef) [ + visibility = JvmVisibility.PRIVATE + body = '''nameBindings.put("«metaVarName»", valueof_name_«metaVarName»());''' + ] + } else if (metaVar.type == MetaVariableType.TYPE) { + members += rule.toMethod("valueof_type_" + metaVarName, typeof(Type).typeRef) [ + visibility = JvmVisibility.PRIVATE + body = assignment.value + ] + callings = callings + "set_type_" + metaVarName + "();" + endl + members += rule.toMethod("set_type_" + metaVarName, typeof(void).typeRef) [ + visibility = JvmVisibility.PRIVATE + body = '''typeBindings.put("«metaVarName»", valueof_type_«metaVarName»());''' + ] + } else if (metaVar.type == MetaVariableType.PARAMETER) { + members += rule.toMethod("valueof_parameter_" + metaVarName, typeof(List).typeRef) [ + visibility = JvmVisibility.PRIVATE + body = assignment.value + ] + callings = callings + "set_parameter_" + metaVarName + "();" + endl + members += rule.toMethod("set_parameter_" + metaVarName, typeof(void).typeRef) [ + visibility = JvmVisibility.PRIVATE + body = '''parameterBindings.put("«metaVarName»", valueof_parameter_«metaVarName»());''' + ] + } else if (metaVar.type == MetaVariableType.VISIBILITY) { + members += rule.toMethod("valueof_visibility_" + metaVarName, typeof(List).typeRef) [ + visibility = JvmVisibility.PRIVATE + body = assignment.value + ] + callings = callings + "set_visibility_" + metaVarName + "();" + endl + members += rule.toMethod("set_visibility_" + metaVarName, typeof(void).typeRef) [ + visibility = JvmVisibility.PRIVATE + body = '''visibilityBindings.put("«metaVarName»", valueof_visibility_«metaVarName»());''' + ] + } else if(metaVar.type == MetaVariableType.ARGUMENT) { + members += rule.toMethod("valueof_argument_" + metaVarName, typeof(List).typeRef) [ + visibility = JvmVisibility.PRIVATE + body = assignment.value + ] + callings = callings + "set_argument_" + metaVarName + "();" + endl + members += rule.toMethod("set_argument_" + metaVarName, typeof(void).typeRef) [ + visibility = JvmVisibility.PRIVATE + body = '''argumentBindings.put("«metaVarName»", valueof_argument_«metaVarName»());''' + ] + } + } + } + + if(rule.definitionPattern !== null && (rule.type == SchemeType.LAMBDA || rule.type == SchemeType.CLASS) ) { + callings = callings + '''super.definitionString = "«rule.definitionPattern.serialize.trim»";'''+ endl + } + + val finalCallings = callings + endl + if (finalCallings.length > 2 || matchingTypeReferenceString.length > 0 || replacementTypeReferenceString.length > 0 || targetTypeReferenceString.length > 0 || definitionTypeReferenceString.length > 0) { + members += rule.toMethod("setMetaVariables", typeof(void).typeRef) [ + //annotations += annotationRef(Override) + visibility = JvmVisibility.PROTECTED + body = '''«finalCallings»super.matchingTypeReferenceString = "«matchingTypeReferenceString»"; +super.replacementTypeReferenceString = "«replacementTypeReferenceString»"; +super.targetTypeReferenceString = "«targetTypeReferenceString»"; +super.definitionTypeReferenceString = "«definitionTypeReferenceString»";''' + ] + } + if (rule.precondition !== null) { members += rule.toMethod("instanceCheck", Boolean.TYPE.typeRef) [ visibility = JvmVisibility.PRIVATE @@ -37,11 +137,24 @@ class RefJavaJvmModelInferrer extends AbstractModelInferrer { body = '''return super.check() && instanceCheck();''' ] } + + if (rule.targetPattern !== null) { + members += rule.toMethod("safeTargetCheck", Boolean.TYPE.typeRef) [ + annotations += annotationRef(Override) + visibility = JvmVisibility.PROTECTED + body = '''return super.targetCheck("«rule.targetPattern.serialize.trim»");''' + ] + } ] } def private toSuperType(SchemeType it) { - LocalRefactoring + switch it { + case LOCAL : LocalRefactoring + case BLOCK : BlockRefactoring + case LAMBDA : LambdaRefactoring + case CLASS : ClassRefactoring + } } } diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/lang/scoping/RefJavaImplicitlyImportedFeatures.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/lang/scoping/RefJavaImplicitlyImportedFeatures.xtend index f1e1572..201bf59 100644 --- a/hu.elte.refjava.lang/src/hu/elte/refjava/lang/scoping/RefJavaImplicitlyImportedFeatures.xtend +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/lang/scoping/RefJavaImplicitlyImportedFeatures.xtend @@ -1,12 +1,11 @@ package hu.elte.refjava.lang.scoping -import org.eclipse.xtext.xbase.scoping.batch.ImplicitlyImportedFeatures import hu.elte.refjava.api.Check +import org.eclipse.xtext.xbase.scoping.batch.ImplicitlyImportedFeatures class RefJavaImplicitlyImportedFeatures extends ImplicitlyImportedFeatures { override protected getStaticImportClasses() { (super.getStaticImportClasses() + #[Check]).toList } - } diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/lang/typesystem/RefJavaTypeComputer.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/lang/typesystem/RefJavaTypeComputer.xtend index ac90341..2bc95d8 100644 --- a/hu.elte.refjava.lang/src/hu/elte/refjava/lang/typesystem/RefJavaTypeComputer.xtend +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/lang/typesystem/RefJavaTypeComputer.xtend @@ -1,8 +1,13 @@ package hu.elte.refjava.lang.typesystem import hu.elte.refjava.lang.refJava.MetaVariable +import hu.elte.refjava.lang.refJava.MetaVariableType +import hu.elte.refjava.lang.refJava.TargetExpression import java.util.List import org.eclipse.jdt.core.dom.ASTNode +import org.eclipse.jdt.core.dom.Expression +import org.eclipse.jdt.core.dom.SingleVariableDeclaration +import org.eclipse.jdt.core.dom.Type import org.eclipse.xtext.xbase.typesystem.computation.ITypeComputationState import org.eclipse.xtext.xbase.typesystem.computation.XbaseTypeComputer import org.eclipse.xtext.xbase.typesystem.references.ParameterizedTypeReference @@ -10,17 +15,44 @@ import org.eclipse.xtext.xbase.typesystem.references.ParameterizedTypeReference class RefJavaTypeComputer extends XbaseTypeComputer { def dispatch computeTypes(MetaVariable metaVar, ITypeComputationState state) { - val astNodeType = getTypeForName(ASTNode, state) - - val type = if (!metaVar.multi) { - astNodeType - } else { + if (metaVar.type == MetaVariableType.CODE) { + val astNodeType = getTypeForName(ASTNode, state) + val type = if (!metaVar.multi) { + astNodeType + } else { + val listType = getTypeForName(List, state) as ParameterizedTypeReference + listType.addTypeArgument(astNodeType) + listType + } + state.acceptActualType(type) + + } else if (metaVar.type == MetaVariableType.NAME) { + val stringType = getTypeForName(String, state) + state.acceptActualType(stringType) + + } else if (metaVar.type == MetaVariableType.TYPE) { + val typeType = getTypeForName(Type, state) + state.acceptActualType(typeType) + + } else if (metaVar.type == MetaVariableType.PARAMETER) { + val parameterType = getTypeForName(SingleVariableDeclaration, state) + val listType = getTypeForName(List, state) as ParameterizedTypeReference + listType.addTypeArgument(parameterType) + state.acceptActualType(listType) + + } else if(metaVar.type == MetaVariableType.ARGUMENT) { + val expressionType = getTypeForName(Expression, state) val listType = getTypeForName(List, state) as ParameterizedTypeReference - listType.addTypeArgument(astNodeType) - listType + listType.addTypeArgument(expressionType) + state.acceptActualType(listType) } + } + def dispatch computeTypes(TargetExpression targetExpr, ITypeComputationState state) { + val astNodeType = getTypeForName(ASTNode, state) + val listType = getTypeForName(List, state) as ParameterizedTypeReference + listType.addTypeArgument(astNodeType) + val type = listType state.acceptActualType(type) } - } diff --git a/hu.elte.refjava.lang/src/hu/elte/refjava/lang/validation/RefJavaValidator.xtend b/hu.elte.refjava.lang/src/hu/elte/refjava/lang/validation/RefJavaValidator.xtend index e55f28a..766ec3e 100644 --- a/hu.elte.refjava.lang/src/hu/elte/refjava/lang/validation/RefJavaValidator.xtend +++ b/hu.elte.refjava.lang/src/hu/elte/refjava/lang/validation/RefJavaValidator.xtend @@ -1,7 +1,10 @@ package hu.elte.refjava.lang.validation import hu.elte.refjava.lang.refJava.MetaVariable +import hu.elte.refjava.lang.refJava.PBlockExpression +import hu.elte.refjava.lang.refJava.PExpression import hu.elte.refjava.lang.refJava.PMetaVariable +import hu.elte.refjava.lang.refJava.PTargetExpression import hu.elte.refjava.lang.refJava.RefJavaPackage import hu.elte.refjava.lang.refJava.SchemeInstanceRule import org.eclipse.emf.ecore.EAttribute @@ -10,39 +13,225 @@ import org.eclipse.xtext.EcoreUtil2 import org.eclipse.xtext.validation.Check import static hu.elte.refjava.lang.refJava.RefJavaPackage.Literals.* +import hu.elte.refjava.lang.refJava.SchemeType +import hu.elte.refjava.lang.refJava.PNothingExpression +import hu.elte.refjava.lang.refJava.PFeatureCall +import hu.elte.refjava.lang.refJava.PMethodDeclaration +import hu.elte.refjava.lang.refJava.PVariableDeclaration +import hu.elte.refjava.lang.refJava.PMemberFeatureCall +import hu.elte.refjava.lang.refJava.PConstructorCall +import hu.elte.refjava.lang.refJava.MetaVariableType +import hu.elte.refjava.api.patterns.Utils class RefJavaValidator extends AbstractRefJavaValidator { - + @Check def checkMetaVariableUniqueness(SchemeInstanceRule schemeInstanceRule) { val matchingMetaVars = EcoreUtil2.getAllContentsOfType(schemeInstanceRule.matchingPattern, PMetaVariable) matchingMetaVars.forEach [ inspectedMetaVar | - if (matchingMetaVars.exists[name == inspectedMetaVar.name && it != inspectedMetaVar]) { + if (matchingMetaVars.exists[name == inspectedMetaVar.name && type == inspectedMetaVar.type && it != inspectedMetaVar]) { error("Duplicate metavariable " + inspectedMetaVar.name, inspectedMetaVar, RefJavaPackage.Literals.PMETA_VARIABLE__NAME) } ] } - + @Check def checkMetaVariableReferences(SchemeInstanceRule schemeInstanceRule) { val matchingMetaVars = EcoreUtil2.getAllContentsOfType(schemeInstanceRule.matchingPattern, PMetaVariable) - val metaVarChecker = [ EObject inspectedMetaVar, String inspectedName, boolean inspectedMulti, + val metaVarChecker = [ EObject inspectedMetaVar, String inspectedName, MetaVariableType inspectedType, boolean inspectedMulti, EAttribute nameFeature, EAttribute multiFeature | val referencedMetaVar = matchingMetaVars.findFirst[name == inspectedName] - if (referencedMetaVar === null) { + if (inspectedType == MetaVariableType.CODE && referencedMetaVar === null) { error("Metavariable " + inspectedName + " cannot be resolved", inspectedMetaVar, nameFeature) - } else if (inspectedMulti != referencedMetaVar.multi) { + } else if (inspectedType == MetaVariableType.CODE && inspectedMulti != referencedMetaVar.multi) { error("Metavariable " + inspectedName + " has wrong multiplicity", inspectedMetaVar, multiFeature) } ] EcoreUtil2.getAllContentsOfType(schemeInstanceRule.replacementPattern, PMetaVariable).forEach [ - metaVarChecker.apply(it, name, multi, PMETA_VARIABLE__NAME, PMETA_VARIABLE__MULTI) + metaVarChecker.apply(it, name, type, multi, PMETA_VARIABLE__NAME, PMETA_VARIABLE__MULTI) ] EcoreUtil2.getAllContentsOfType(schemeInstanceRule.precondition, MetaVariable).forEach [ - metaVarChecker.apply(it, name, multi, META_VARIABLE__NAME, META_VARIABLE__MULTI) + metaVarChecker.apply(it, name, type, multi, META_VARIABLE__NAME, META_VARIABLE__MULTI) ] } + + @Check + def targetPatternChecker(SchemeInstanceRule schemeInstanceRule) { + if (schemeInstanceRule.type == SchemeType.LOCAL && schemeInstanceRule.targetPattern !== null) { + + error("A local refactoring scheme cannot have a target closure.", schemeInstanceRule, SCHEME_INSTANCE_RULE__TYPE) + + } else { + if (schemeInstanceRule.targetPattern !== null) { + if (EcoreUtil2.getAllContentsOfType(schemeInstanceRule.targetPattern, PTargetExpression) !== null) { + EcoreUtil2.getAllContentsOfType(schemeInstanceRule.targetPattern, PTargetExpression).forEach[ + error("The target pattern cannot contain a target expression.", it, PTARGET_EXPRESSION.getEStructuralFeature(0)) + ] + } + } + } + } + + //checks the attribute-binding meta variable type correctness + @Check + def metaVariableTypeCorrectnessChecker(SchemeInstanceRule schemeInstanceRule) { + schemeInstanceRule.matchingPattern.patterns.forEach[hasCorrectMetaVariableTypes] + schemeInstanceRule.replacementPattern.patterns.forEach[hasCorrectMetaVariableTypes] + schemeInstanceRule.targetPattern.patterns.forEach[hasCorrectMetaVariableTypes] + schemeInstanceRule.definitionPattern.patterns.forEach[hasCorrectMetaVariableTypes] + } + + def void hasCorrectMetaVariableTypes(PExpression expression) { + if (expression instanceof PMethodDeclaration) { + val method = expression as PMethodDeclaration + if (method.metaParameters !== null && (method.metaParameters as PMetaVariable).type != MetaVariableType.PARAMETER) { + error("The type of the meta variable should be 'parameter' here.", method.metaParameters, PMETA_VARIABLE__TYPE) + } + method.prefix.hasCorrectMetaVariableTypes + } else if (expression instanceof PVariableDeclaration) { + val varDecl = expression as PVariableDeclaration + if(varDecl.metaName !== null && (varDecl.metaName as PMetaVariable).type != MetaVariableType.NAME) { + error("The type of the meta variable should be 'name' here.", varDecl.metaName, PMETA_VARIABLE__TYPE) + } + if(varDecl.metaType !== null && (varDecl.metaType as PMetaVariable).type != MetaVariableType.TYPE) { + error("The type of the meta variable should be 'type' here.", varDecl.metaType, PMETA_VARIABLE__TYPE) + } + if(varDecl.metaVisibility !== null && (varDecl.metaVisibility as PMetaVariable).type != MetaVariableType.VISIBILITY) { + error("The type of the meta variable should be 'visibility' here.", varDecl.metaVisibility, PMETA_VARIABLE__TYPE) + } + } else if (expression instanceof PMemberFeatureCall) { + val memberFeatureCall = expression as PMemberFeatureCall + if(memberFeatureCall.metaFeature !== null && (memberFeatureCall.metaFeature as PMetaVariable).type != MetaVariableType.NAME) { + error("The type of the meta variable should be 'name' here.", memberFeatureCall.metaFeature, PMETA_VARIABLE__TYPE) + } + if(memberFeatureCall.memberCallArguments !== null && (memberFeatureCall.memberCallArguments as PMetaVariable).type != MetaVariableType.ARGUMENT) { + error("The type of the meta variable should be 'argument' here.", memberFeatureCall.memberCallArguments, PMETA_VARIABLE__TYPE) + } + memberFeatureCall.memberCallTarget.hasCorrectMetaVariableTypes + } else if (expression instanceof PConstructorCall) { + val constructorCall = expression as PConstructorCall + if (constructorCall.metaName !== null && (constructorCall.metaName as PMetaVariable).type != MetaVariableType.NAME) { + error("The type of the meta variable should be 'name' here.", constructorCall.metaName, PMETA_VARIABLE__TYPE) + } + if (constructorCall.arguments !== null && (constructorCall.arguments as PMetaVariable).type != MetaVariableType.ARGUMENT) { + error("The type of the meta variable should be 'argument' here.", constructorCall.arguments, PMETA_VARIABLE__TYPE) + } + if (constructorCall.anonInstance) { + constructorCall.elements.forEach[hasCorrectMetaVariableTypes] + } + } else if (expression instanceof PBlockExpression) { + val block = expression as PBlockExpression + block.expressions.forEach[hasCorrectMetaVariableTypes] + } else if (expression instanceof PFeatureCall) { + val featureCall = expression as PFeatureCall + if(featureCall.metaFeature !== null && (featureCall.metaFeature as PMetaVariable).type != MetaVariableType.NAME) { + error("The type of the meta variable should be 'name' here.", featureCall.metaFeature, PMETA_VARIABLE__TYPE) + } + if(featureCall.featureCallArguments !== null && (featureCall.featureCallArguments as PMetaVariable).type != MetaVariableType.ARGUMENT) { + error("The type of the meta variable should be 'argument' here.", featureCall.featureCallArguments, PMETA_VARIABLE__TYPE) + } + } + } + + @Check + def parameterAndArgumentMetaVariableMultiplicity(SchemeInstanceRule schemeInstanceRule) { + EcoreUtil2.getAllContentsOfType(schemeInstanceRule.matchingPattern, PMetaVariable).forEach[hasCorrectMultiplicity] + EcoreUtil2.getAllContentsOfType(schemeInstanceRule.replacementPattern, PMetaVariable).forEach[hasCorrectMultiplicity] + EcoreUtil2.getAllContentsOfType(schemeInstanceRule.targetPattern, PMetaVariable).forEach[hasCorrectMultiplicity] + EcoreUtil2.getAllContentsOfType(schemeInstanceRule.definitionPattern, PMetaVariable).forEach[hasCorrectMultiplicity] + } + + def hasCorrectMultiplicity(PMetaVariable metaVar) { + if(metaVar.type == MetaVariableType.PARAMETER && !metaVar.multi) { + error("A parameter-binding meta variable should always have multiplicity.", metaVar, PMETA_VARIABLE__MULTI) + } else if (metaVar.type == MetaVariableType.ARGUMENT && !metaVar.multi) { + error("An argument-binding meta variable should always have multiplicity.", metaVar, PMETA_VARIABLE__MULTI) + } else if (metaVar.type == MetaVariableType.NAME && metaVar.multi) { + error("A name-binding meta variable cannot have multiplicity.", metaVar, PMETA_VARIABLE__MULTI) + } else if (metaVar.type == MetaVariableType.TYPE && metaVar.multi) { + error("A type-binding meta variable cannot have multiplicity.", metaVar, PMETA_VARIABLE__MULTI) + } else if (metaVar.type == MetaVariableType.VISIBILITY && metaVar.multi) { + error("A visibility-binding meta variable cannot have multiplicity.", metaVar, PMETA_VARIABLE__MULTI) + } + } + + + //class and lambda refactorings pattern limitations + @Check + def patternLimitationsChecker(SchemeInstanceRule schemeInstanceRule) { + if(schemeInstanceRule.type == SchemeType.CLASS) { + if (!(schemeInstanceRule.replacementPattern.patterns.head instanceof PNothingExpression) + && !(schemeInstanceRule.replacementPattern.patterns.head instanceof PFeatureCall) + || schemeInstanceRule.replacementPattern.patterns.size > 1) { + + error("A class refactoring's replacement pattern can only be either a single nothing expression or a single feature call.", + schemeInstanceRule.replacementPattern, PATTERN.getEStructuralFeature(0)) + + } else if (schemeInstanceRule.replacementPattern.patterns.head instanceof PNothingExpression + && !(schemeInstanceRule.matchingPattern.patterns.head instanceof PMethodDeclaration) + && !(schemeInstanceRule.matchingPattern.patterns.head instanceof PVariableDeclaration) + || schemeInstanceRule.matchingPattern.patterns.size > 1) { + error("The matching pattern can only be either a single method declaration or a single variable declaration, if the replacement pattern is a nothing expression.", + schemeInstanceRule.matchingPattern, PATTERN.getEStructuralFeature(0)) + } + } else if (schemeInstanceRule.type == SchemeType.LAMBDA) { + + if (!Utils.isValidLambdaExpression(schemeInstanceRule.replacementPattern.patterns.head) || schemeInstanceRule.replacementPattern.patterns.size > 1) { + error("A lambda refactoring's replacement pattern can only be a single valid lambda expression. +Example: new F() { public void apply() { } }.apply()", schemeInstanceRule.replacementPattern, PATTERN.getEStructuralFeature(0)) + + } else if (schemeInstanceRule.matchingPattern.patterns.exists[Utils.isValidLambdaExpression(it)] && schemeInstanceRule.matchingPattern.patterns.size > 1) { + + error("The matching pattern can be either a single lambda expression, or a pattern that doesn't contains a lambda expression.", schemeInstanceRule.replacementPattern, PATTERN.getEStructuralFeature(0)) + + } else if (!((schemeInstanceRule.replacementPattern.patterns.head as PMemberFeatureCall).memberCallTarget as PConstructorCall).elements.exists[ + it instanceof PMethodDeclaration && ((it as PMethodDeclaration).prefix.name == (schemeInstanceRule.replacementPattern.patterns.head as PMemberFeatureCall).feature + && ((it as PMethodDeclaration).prefix.metaName as PMetaVariable).name == ((schemeInstanceRule.replacementPattern.patterns.head as PMemberFeatureCall).metaFeature as PMetaVariable).name)]) { + + error("The feature call's name can only be an existing method inside the lambda expression.", schemeInstanceRule.replacementPattern, PATTERN.getEStructuralFeature(0)) + + } else if (Utils.isValidLambdaExpression(schemeInstanceRule.matchingPattern.patterns.head) && !((schemeInstanceRule.matchingPattern.patterns.head as PMemberFeatureCall).memberCallTarget as PConstructorCall).elements.exists[ + it instanceof PMethodDeclaration && ((it as PMethodDeclaration).prefix.name == (schemeInstanceRule.matchingPattern.patterns.head as PMemberFeatureCall).feature + && ((it as PMethodDeclaration).prefix.metaName as PMetaVariable).name == ((schemeInstanceRule.matchingPattern.patterns.head as PMemberFeatureCall).metaFeature as PMetaVariable).name)]) { + + error("The feature call's name can only be an existing method inside the lambda expression.", schemeInstanceRule.matchingPattern, PATTERN.getEStructuralFeature(0)) + + } else if (Utils.isValidLambdaExpression(schemeInstanceRule.matchingPattern.patterns.head) && schemeInstanceRule.matchingPattern.patterns.size > 1) { + error("The matching pattern's length can only be single, if the matching pattern is meant to be a lambda expression.", schemeInstanceRule.matchingPattern, PATTERN.getEStructuralFeature(0)) + } + } + } + + @Check + def multiMetavariableCountValidation(SchemeInstanceRule schemeInstanceRule) { + val matchingPatterns = schemeInstanceRule.matchingPattern.patterns + val targetExpressions = EcoreUtil2.getAllContentsOfType(schemeInstanceRule.matchingPattern, PTargetExpression) + if(targetExpressions.size != 0) { + + if (targetExpressions.size > 1) { + targetExpressions.forEach[error("Two or more target expression within the same matching pattern doesn't make sense.", it, PTARGET_EXPRESSION.getEStructuralFeature(0) )] + } + val preTargetExpressions = matchingPatterns.clone.takeWhile[ !(it instanceof PTargetExpression)] + val postTargetExpressions = matchingPatterns.clone.reverse.takeWhile[ !(it instanceof PTargetExpression)] + multiMetavarCountChecker(preTargetExpressions) + multiMetavarCountChecker(postTargetExpressions) + + } else { + multiMetavarCountChecker(matchingPatterns) + } + } + + def void multiMetavarCountChecker(Iterable expressions) { + val multiMetavars = expressions.filter[it instanceof PMetaVariable && (it as PMetaVariable).multi] + val blocks = expressions.filter[it instanceof PBlockExpression] + if (multiMetavars.size > 1) { + multiMetavars.forEach[error("Two or more metavariable with multiplicity in the same scope doesn't make sense. +If the matching pattern has a target expression, then there cannot be two or more +metavariable with multiplicity before, and after the target expression.", it, PMETA_VARIABLE__MULTI)] + } + blocks.forEach[multiMetavarCountChecker( (it as PBlockExpression).expressions )] + } } diff --git a/hu.elte.refjava.update/.project b/hu.elte.refjava.update/.project new file mode 100644 index 0000000..530e02b --- /dev/null +++ b/hu.elte.refjava.update/.project @@ -0,0 +1,17 @@ + + + hu.elte.refjava.update + + + + + + org.eclipse.pde.UpdateSiteBuilder + + + + + + org.eclipse.pde.UpdateSiteNature + + diff --git a/hu.elte.refjava.update/artifacts.jar b/hu.elte.refjava.update/artifacts.jar new file mode 100644 index 0000000..89a7702 Binary files /dev/null and b/hu.elte.refjava.update/artifacts.jar differ diff --git a/hu.elte.refjava.update/content.jar b/hu.elte.refjava.update/content.jar new file mode 100644 index 0000000..c7721a0 Binary files /dev/null and b/hu.elte.refjava.update/content.jar differ diff --git a/hu.elte.refjava.update/features/hu.elte.refjava.feature_1.0.0.202005151427.jar b/hu.elte.refjava.update/features/hu.elte.refjava.feature_1.0.0.202005151427.jar new file mode 100644 index 0000000..7e94b85 Binary files /dev/null and b/hu.elte.refjava.update/features/hu.elte.refjava.feature_1.0.0.202005151427.jar differ diff --git a/hu.elte.refjava.update/plugins/hu.elte.refjava.control_1.0.0.202005151427.jar b/hu.elte.refjava.update/plugins/hu.elte.refjava.control_1.0.0.202005151427.jar new file mode 100644 index 0000000..755c8dd Binary files /dev/null and b/hu.elte.refjava.update/plugins/hu.elte.refjava.control_1.0.0.202005151427.jar differ diff --git a/hu.elte.refjava.update/plugins/hu.elte.refjava.lang.ide_1.0.0.202005151427.jar b/hu.elte.refjava.update/plugins/hu.elte.refjava.lang.ide_1.0.0.202005151427.jar new file mode 100644 index 0000000..5b3a1bd Binary files /dev/null and b/hu.elte.refjava.update/plugins/hu.elte.refjava.lang.ide_1.0.0.202005151427.jar differ diff --git a/hu.elte.refjava.update/plugins/hu.elte.refjava.lang.ui_1.0.0.202005151427.jar b/hu.elte.refjava.update/plugins/hu.elte.refjava.lang.ui_1.0.0.202005151427.jar new file mode 100644 index 0000000..4c36fd5 Binary files /dev/null and b/hu.elte.refjava.update/plugins/hu.elte.refjava.lang.ui_1.0.0.202005151427.jar differ diff --git a/hu.elte.refjava.update/plugins/hu.elte.refjava_1.0.0.202005151427.jar b/hu.elte.refjava.update/plugins/hu.elte.refjava_1.0.0.202005151427.jar new file mode 100644 index 0000000..34ade4b Binary files /dev/null and b/hu.elte.refjava.update/plugins/hu.elte.refjava_1.0.0.202005151427.jar differ diff --git a/hu.elte.refjava.update/site.xml b/hu.elte.refjava.update/site.xml new file mode 100644 index 0000000..dc6e238 --- /dev/null +++ b/hu.elte.refjava.update/site.xml @@ -0,0 +1,7 @@ + + + + + + +