J2K converter: automatic generation of 'open' modifier for classes and methods

This commit is contained in:
Valentin Kipyatkov
2014-10-24 18:05:22 +04:00
committed by valentin
parent 39cea5999c
commit 7e147d28c2
34 changed files with 206 additions and 86 deletions
@@ -47,7 +47,8 @@ enum class AccessorKind {
} }
class ClassBodyConverter(private val psiClass: PsiClass, class ClassBodyConverter(private val psiClass: PsiClass,
private val converter: Converter) { private val converter: Converter,
private val isOpenClass: Boolean) {
private val membersToRemove = HashSet<PsiMember>() private val membersToRemove = HashSet<PsiMember>()
private val fieldCorrections = HashMap<PsiField, FieldCorrectionInfo>() private val fieldCorrections = HashMap<PsiField, FieldCorrectionInfo>()
@@ -110,7 +111,7 @@ class ClassBodyConverter(private val psiClass: PsiClass,
membersToRemove: MutableSet<PsiMember>, membersToRemove: MutableSet<PsiMember>,
constructorConverter: ConstructorConverter?): Member? { constructorConverter: ConstructorConverter?): Member? {
return when (member) { return when (member) {
is PsiMethod -> convertMethod(member, membersToRemove, constructorConverter) is PsiMethod -> convertMethod(member, membersToRemove, constructorConverter, isOpenClass)
is PsiField -> convertField(member, fieldCorrections[member]) is PsiField -> convertField(member, fieldCorrections[member])
is PsiClass -> convertClass(member) is PsiClass -> convertClass(member)
is PsiClassInitializer -> convertInitializer(member) is PsiClassInitializer -> convertInitializer(member)
@@ -148,7 +148,7 @@ class CodeConverter(public val converter: Converter,
} }
public fun convertAnonymousClassBody(anonymousClass: PsiAnonymousClass): AnonymousClassBody { public fun convertAnonymousClassBody(anonymousClass: PsiAnonymousClass): AnonymousClassBody {
return AnonymousClassBody(ClassBodyConverter(anonymousClass, converter).convertBody(), return AnonymousClassBody(ClassBodyConverter(anonymousClass, converter, false).convertBody(),
anonymousClass.getBaseClassType().resolve()?.isInterface() ?: false).assignPrototype(anonymousClass) anonymousClass.getBaseClassType().resolve()?.isInterface() ?: false).assignPrototype(anonymousClass)
} }
+41 -8
View File
@@ -95,7 +95,7 @@ class Converter private(private val elementToConvert: PsiElement,
private fun convertTopElement(element: PsiElement): Element? = when (element) { private fun convertTopElement(element: PsiElement): Element? = when (element) {
is PsiJavaFile -> convertFile(element) is PsiJavaFile -> convertFile(element)
is PsiClass -> convertClass(element) is PsiClass -> convertClass(element)
is PsiMethod -> convertMethod(element, null, null) is PsiMethod -> convertMethod(element, null, null, false)
is PsiField -> convertField(element, null) is PsiField -> convertField(element, null)
is PsiStatement -> createDefaultCodeConverter().convertStatement(element) is PsiStatement -> createDefaultCodeConverter().convertStatement(element)
is PsiExpression -> createDefaultCodeConverter().convertExpression(element) is PsiExpression -> createDefaultCodeConverter().convertExpression(element)
@@ -179,15 +179,19 @@ class Converter private(private val elementToConvert: PsiElement,
val extendsTypes = convertToNotNullableTypes(psiClass.getExtendsListTypes()) val extendsTypes = convertToNotNullableTypes(psiClass.getExtendsListTypes())
val name = psiClass.declarationIdentifier() val name = psiClass.declarationIdentifier()
var classBody = ClassBodyConverter(psiClass, this).convertBody()
return when { return when {
psiClass.isInterface() -> Trait(name, annotations, modifiers, typeParameters, extendsTypes, listOf(), implementsTypes, classBody) psiClass.isInterface() -> {
var classBody = ClassBodyConverter(psiClass, this, false).convertBody()
Trait(name, annotations, modifiers, typeParameters, extendsTypes, listOf(), implementsTypes, classBody)
}
psiClass.isEnum() -> Enum(name, annotations, modifiers, typeParameters, listOf(), listOf(), implementsTypes, classBody) psiClass.isEnum() -> {
var classBody = ClassBodyConverter(psiClass, this, false).convertBody()
Enum(name, annotations, modifiers, typeParameters, listOf(), listOf(), implementsTypes, classBody)
}
else -> { else -> {
if (settings.openByDefault && !psiClass.hasModifierProperty(PsiModifier.FINAL)) { if (needOpenModifier(psiClass)) {
modifiers = modifiers.with(Modifier.OPEN) modifiers = modifiers.with(Modifier.OPEN)
} }
@@ -195,11 +199,20 @@ class Converter private(private val elementToConvert: PsiElement,
modifiers = modifiers.with(Modifier.INNER) modifiers = modifiers.with(Modifier.INNER)
} }
var classBody = ClassBodyConverter(psiClass, this, modifiers.contains(Modifier.OPEN)).convertBody()
Class(name, annotations, modifiers, typeParameters, extendsTypes, classBody.baseClassParams, implementsTypes, classBody) Class(name, annotations, modifiers, typeParameters, extendsTypes, classBody.baseClassParams, implementsTypes, classBody)
} }
}.assignPrototype(psiClass) }.assignPrototype(psiClass)
} }
private fun needOpenModifier(psiClass: PsiClass): Boolean {
return if (settings.openByDefault)
!psiClass.hasModifierProperty(PsiModifier.FINAL) && !psiClass.hasModifierProperty(PsiModifier.ABSTRACT)
else
referenceSearcher.hasInheritors(psiClass)
}
private fun convertAnnotationType(psiClass: PsiClass): Class { private fun convertAnnotationType(psiClass: PsiClass): Class {
val paramModifiers = Modifiers(listOf(Modifier.PUBLIC)).assignNoPrototype() val paramModifiers = Modifiers(listOf(Modifier.PUBLIC)).assignNoPrototype()
val noBlankLinesInheritance = CommentsAndSpacesInheritance(blankLinesBefore = false) val noBlankLinesInheritance = CommentsAndSpacesInheritance(blankLinesBefore = false)
@@ -224,7 +237,7 @@ class Converter private(private val elementToConvert: PsiElement,
val constructorSignature = PrimaryConstructorSignature(Annotations.Empty(), Modifiers.Empty(), parameterList).assignNoPrototype() val constructorSignature = PrimaryConstructorSignature(Annotations.Empty(), Modifiers.Empty(), parameterList).assignNoPrototype()
// to convert fields and nested types - they are not allowed in Kotlin but we convert them and let user refactor code // to convert fields and nested types - they are not allowed in Kotlin but we convert them and let user refactor code
var classBody = ClassBodyConverter(psiClass, this).convertBody() var classBody = ClassBodyConverter(psiClass, this, false).convertBody()
classBody = ClassBody(constructorSignature, classBody.baseClassParams, classBody.members, classBody.classObjectMembers, listOf(), classBody.lBrace, classBody.rBrace) classBody = ClassBody(constructorSignature, classBody.baseClassParams, classBody.members, classBody.classObjectMembers, listOf(), classBody.lBrace, classBody.rBrace)
val annotationAnnotation = Annotation(Identifier("annotation").assignNoPrototype(), listOf(), false, false).assignNoPrototype() val annotationAnnotation = Annotation(Identifier("annotation").assignNoPrototype(), listOf(), false, false).assignNoPrototype()
@@ -299,11 +312,18 @@ class Converter private(private val elementToConvert: PsiElement,
return if (convertedType == initializerType) null else convertedType return if (convertedType == initializerType) null else convertedType
} }
public fun convertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>?, constructorConverter: ConstructorConverter?): Member? { public fun convertMethod(method: PsiMethod,
membersToRemove: MutableSet<PsiMember>?,
constructorConverter: ConstructorConverter?,
isInOpenClass: Boolean): Member? {
val returnType = typeConverter.convertMethodReturnType(method) val returnType = typeConverter.convertMethodReturnType(method)
val annotations = (convertAnnotations(method) + convertThrows(method)).assignNoPrototype() val annotations = (convertAnnotations(method) + convertThrows(method)).assignNoPrototype()
var modifiers = convertModifiers(method) var modifiers = convertModifiers(method)
if (needOpenModifier(method, isInOpenClass, modifiers)) {
modifiers = modifiers.with(Modifier.OPEN)
}
val statementsToInsert = ArrayList<Statement>() val statementsToInsert = ArrayList<Statement>()
for (parameter in method.getParameterList().getParameters()) { for (parameter in method.getParameterList().getParameters()) {
@@ -392,6 +412,19 @@ class Converter private(private val elementToConvert: PsiElement,
return false return false
} }
private fun needOpenModifier(method: PsiMethod, isInOpenClass: Boolean, modifiers: Modifiers): Boolean {
if (!isInOpenClass) return false
if (modifiers.contains(Modifier.OVERRIDE) || modifiers.contains(Modifier.ABSTRACT)) return false
if (settings.openByDefault) {
return !method.hasModifierProperty(PsiModifier.FINAL)
&& !method.hasModifierProperty(PsiModifier.PRIVATE)
&& !method.hasModifierProperty(PsiModifier.STATIC)
}
else {
return referenceSearcher.hasOverrides(method)
}
}
public fun convertCodeReferenceElement(element: PsiJavaCodeReferenceElement, hasExternalQualifier: Boolean, typeArgsConverted: List<Element>? = null): ReferenceElement { public fun convertCodeReferenceElement(element: PsiJavaCodeReferenceElement, hasExternalQualifier: Boolean, typeArgsConverted: List<Element>? = null): ReferenceElement {
val typeArgs = typeArgsConverted ?: typeConverter.convertTypes(element.getTypeParameters()) val typeArgs = typeArgsConverted ?: typeConverter.convertTypes(element.getTypeParameters())
@@ -23,16 +23,22 @@ import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiMethodCallExpression import com.intellij.psi.PsiMethodCallExpression
import java.util.Collections import com.intellij.psi.PsiClass
import com.intellij.psi.search.searches.ClassInheritorsSearch
import com.intellij.psi.search.searches.OverridingMethodsSearch
public trait ReferenceSearcher { public trait ReferenceSearcher {
fun findVariableUsages(variable: PsiVariable, scope: PsiElement): Collection<PsiReferenceExpression> fun findVariableUsages(variable: PsiVariable, scope: PsiElement): Collection<PsiReferenceExpression>
fun findMethodCalls(method: PsiMethod, scope: PsiElement): Collection<PsiMethodCallExpression> fun findMethodCalls(method: PsiMethod, scope: PsiElement): Collection<PsiMethodCallExpression>
fun hasInheritors(`class`: PsiClass): Boolean
fun hasOverrides(method: PsiMethod): Boolean
} }
public object EmptyReferenceSearcher: ReferenceSearcher { public object EmptyReferenceSearcher: ReferenceSearcher {
override fun findVariableUsages(variable: PsiVariable, scope: PsiElement) = listOf<PsiReferenceExpression>() override fun findVariableUsages(variable: PsiVariable, scope: PsiElement) = listOf<PsiReferenceExpression>()
override fun findMethodCalls(method: PsiMethod, scope: PsiElement) = listOf<PsiMethodCallExpression>() override fun findMethodCalls(method: PsiMethod, scope: PsiElement) = listOf<PsiMethodCallExpression>()
override fun hasInheritors(`class`: PsiClass) = false
override fun hasOverrides(method: PsiMethod) = false
} }
public object IdeaReferenceSearcher : ReferenceSearcher { public object IdeaReferenceSearcher : ReferenceSearcher {
@@ -51,4 +57,8 @@ public object IdeaReferenceSearcher : ReferenceSearcher {
} }
}.filterNotNull() }.filterNotNull()
} }
override fun hasInheritors(`class`: PsiClass) = ClassInheritorsSearch.search(`class`, false).any()
override fun hasOverrides(method: PsiMethod) = OverridingMethodsSearch.search(method, false).any()
} }
@@ -2162,6 +2162,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
String fileName = JetTestUtils.navigationMetadata("j2k/tests/testData/fileOrElement/inheritance/classOneExtendsBaseWithZeroParamsNonEmptyConstructor.java"); String fileName = JetTestUtils.navigationMetadata("j2k/tests/testData/fileOrElement/inheritance/classOneExtendsBaseWithZeroParamsNonEmptyConstructor.java");
doTest(fileName); doTest(fileName);
} }
@TestMetadata("openModifier.java")
public void testOpenModifier() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/tests/testData/fileOrElement/inheritance/openModifier.java");
doTest(fileName);
}
} }
@TestMetadata("j2k/tests/testData/fileOrElement/isOperator") @TestMetadata("j2k/tests/testData/fileOrElement/isOperator")
@@ -2162,6 +2162,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
String fileName = JetTestUtils.navigationMetadata("j2k/tests/testData/fileOrElement/inheritance/classOneExtendsBaseWithZeroParamsNonEmptyConstructor.java"); String fileName = JetTestUtils.navigationMetadata("j2k/tests/testData/fileOrElement/inheritance/classOneExtendsBaseWithZeroParamsNonEmptyConstructor.java");
doTest(fileName); doTest(fileName);
} }
@TestMetadata("openModifier.java")
public void testOpenModifier() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/tests/testData/fileOrElement/inheritance/openModifier.java");
doTest(fileName);
}
} }
@TestMetadata("j2k/tests/testData/fileOrElement/isOperator") @TestMetadata("j2k/tests/testData/fileOrElement/isOperator")
@@ -1,4 +1,3 @@
// ERROR: This type is final, so it cannot be inherited from open class Base(o: Any, l: Int)
class Base(o: Any, l: Int)
class C(private val string: String) : Base(string, string.length()) class C(private val string: String) : Base(string, string.length())
@@ -1,5 +1,4 @@
// ERROR: This type is final, so it cannot be inherited from open class Base(nested: Base.Nested) {
class Base(nested: Base.Nested) {
class Nested(p: Int) { class Nested(p: Int) {
class object { class object {
@@ -1,5 +1,4 @@
// ERROR: This type is final, so it cannot be inherited from public open class Base(x: Int) {
public class Base(x: Int) {
public var x: Int = 42 public var x: Int = 42
protected set protected set
@@ -1,5 +1,4 @@
// ERROR: This type is final, so it cannot be inherited from public open class AAA {
public class AAA {
public var x: Int = 42 public var x: Int = 42
protected set protected set
@@ -1,5 +1,4 @@
// ERROR: This type is final, so it cannot be inherited from open class Base {
class Base {
private val myFirst: String? = null private val myFirst: String? = null
} }
@@ -1,6 +1,3 @@
// ERROR: 'clone' in 'Base' is final and cannot be overridden
// ERROR: 'finalize' in 'Base' is final and cannot be overridden
// ERROR: This type is final, so it cannot be inherited from
// ERROR: Unresolved reference: clone // ERROR: Unresolved reference: clone
// ERROR: Unresolved reference: finalize // ERROR: Unresolved reference: finalize
package test package test
@@ -29,7 +26,7 @@ class Test : Base() {
} }
} }
class Base { open class Base {
override fun hashCode(): Int { override fun hashCode(): Int {
return super.hashCode() return super.hashCode()
} }
@@ -39,7 +36,7 @@ class Base {
} }
throws(javaClass<CloneNotSupportedException>()) throws(javaClass<CloneNotSupportedException>())
protected fun clone(): Any { protected open fun clone(): Any {
return super.clone() return super.clone()
} }
@@ -48,7 +45,7 @@ class Base {
} }
throws(javaClass<Throwable>()) throws(javaClass<Throwable>())
protected fun finalize() { protected open fun finalize() {
super.finalize() super.finalize()
} }
} }
@@ -1,7 +1,5 @@
// ERROR: 'a' in 'A' is final and cannot be overridden open class A {
// ERROR: This type is final, so it cannot be inherited from open fun a() {
class A {
fun a() {
} }
} }
@@ -1,12 +1,9 @@
// ERROR: 'foo' in 'A' is final and cannot be overridden open class A {
// ERROR: This type is final, so it cannot be inherited from open fun foo() {
// ERROR: This type is final, so it cannot be inherited from
class A {
fun foo() {
} }
} }
class B : A() { open class B : A() {
override fun foo() { override fun foo() {
} }
} }
@@ -1,6 +1,5 @@
// ERROR: This type is final, so it cannot be inherited from
// ERROR: Unresolved reference: clone // ERROR: Unresolved reference: clone
class Base open class Base
class X : Base() { class X : Base() {
override fun hashCode(): Int { override fun hashCode(): Int {
@@ -1,5 +1,4 @@
// ERROR: This type is final, so it cannot be inherited from open class Base {
class Base {
override fun equals(o: Any?): Boolean { override fun equals(o: Any?): Boolean {
return super.equals(o) return super.equals(o)
} }
@@ -1,7 +1,6 @@
// ERROR: This type is final, so it cannot be inherited from
class `$$$$$` class `$$$$$`
class `$` open class `$`
class `$$`(val `$$$`: `$$$$$`) : `$`() { class `$$`(val `$$$`: `$$$$$`) : `$`() {
@@ -1,4 +1,3 @@
// ERROR: This type is final, so it cannot be inherited from open class Base<T>(name: T)
class Base<T>(name: T)
class One<T, K>(name: T, private val mySecond: K) : Base<T>(name) class One<T, K>(name: T, private val mySecond: K) : Base<T>(name)
@@ -1,4 +1,3 @@
// ERROR: This type is final, so it cannot be inherited from open class Base(name: String)
class Base(name: String)
class One(name: String, second: String) : Base(name) class One(name: String, second: String) : Base(name)
@@ -1,4 +1,3 @@
// ERROR: This type is final, so it cannot be inherited from open class Base
class Base
class One : Base() class One : Base()
@@ -1,4 +1,3 @@
// ERROR: This type is final, so it cannot be inherited from open class Base(name: String)
class Base(name: String)
class One(name: String, private val mySecond: String) : Base(name) class One(name: String, private val mySecond: String) : Base(name)
@@ -0,0 +1,48 @@
import java.lang.Override;
import java.lang.Void;
class A {
public void f1(){}
public void f2(){}
private void f3(){}
}
class B extends A {
@Override
public void f1() {
super.f1();
}
}
class C extends B {
@Override
public void f1() {
super.f1();
}
}
interface I {
void f();
}
class D implements I {
@Override
public void f() { }
}
abstract class E {
abstract void f1();
void f2(){}
void f3(){}
}
class F extends E {
@Override
void f1() {
}
@Override
void f2() {
super.f2();
}
}
@@ -0,0 +1,51 @@
import java.lang.Void
open class A {
public open fun f1() {
}
public fun f2() {
}
private fun f3() {
}
}
open class B : A() {
override fun f1() {
super.f1()
}
}
class C : B() {
override fun f1() {
super.f1()
}
}
trait I {
public fun f()
}
class D : I {
override fun f() {
}
}
abstract class E {
abstract fun f1()
open fun f2() {
}
fun f3() {
}
}
class F : E() {
override fun f1() {
}
override fun f2() {
super.f2()
}
}
@@ -1,6 +1,5 @@
// ERROR: This type is final, so it cannot be inherited from
// ERROR: Property must be initialized or be abstract // ERROR: Property must be initialized or be abstract
class Base { open class Base {
inner class Nested inner class Nested
} }
@@ -1,7 +1,6 @@
// ERROR: This type is final, so it cannot be inherited from
package test package test
class Base { open class Base {
override fun hashCode(): Int { override fun hashCode(): Int {
return super.hashCode() return super.hashCode()
} }
@@ -1,5 +1,3 @@
// ERROR: 'test' in 'Base' is final and cannot be overridden
// ERROR: This type is final, so it cannot be inherited from
package com.voltvoodoo.saplo4j.model package com.voltvoodoo.saplo4j.model
import java.io.Serializable import java.io.Serializable
@@ -12,8 +10,8 @@ public class Language(protected var code: String) : Serializable {
} }
class Base { open class Base {
fun test() { open fun test() {
} }
override fun toString(): String { override fun toString(): String {
@@ -1,13 +1,11 @@
// ERROR: There's a cycle in the inheritance hierarchy for this type // ERROR: There's a cycle in the inheritance hierarchy for this type
// ERROR: There's a cycle in the inheritance hierarchy for this type // ERROR: There's a cycle in the inheritance hierarchy for this type
// ERROR: This type is final, so it cannot be inherited from open class A : B() {
// ERROR: This type is final, so it cannot be inherited from public open fun foo(s: String) {
class A : B() {
public fun foo(s: String) {
} }
} }
class B : A() { open class B : A() {
public fun foo(s: String) { public open fun foo(s: String) {
} }
} }
@@ -1,16 +1,13 @@
// ERROR: 'foo' in 'Base' is final and cannot be overridden open class Base {
// ERROR: 'bar' in 'Base' is final and cannot be overridden public open fun foo(s: String?): String? {
// ERROR: This type is final, so it cannot be inherited from
class Base {
public fun foo(s: String?): String? {
return "" return ""
} }
public fun bar(s: String?): String? { public open fun bar(s: String?): String? {
return if (s != null) s + 1 else null return if (s != null) s + 1 else null
} }
public fun zoo(o: Any): String { public open fun zoo(o: Any): String {
return "" return ""
} }
} }
@@ -1,18 +1,15 @@
// ERROR: This type is final, so it cannot be inherited from
// ERROR: This type is final, so it cannot be inherited from
// ERROR: This type is final, so it cannot be inherited from
package demo package demo
trait WindowListener { trait WindowListener {
public fun windowClosing() public fun windowClosing()
} }
class WindowAdapter : WindowListener { open class WindowAdapter : WindowListener {
override fun windowClosing() { override fun windowClosing() {
} }
} }
class Frame { open class Frame {
public fun addWindowListener(listener: WindowListener) { public fun addWindowListener(listener: WindowListener) {
} }
} }
@@ -1,7 +1,7 @@
// !openByDefault: true // !openByDefault: true
open class A { open class A {
open fun foo1() { open open fun foo1() {
} }
private fun foo2() { private fun foo2() {
@@ -1,7 +1,6 @@
// ERROR: This type is final, so it cannot be inherited from
package a.b package a.b
class Base { open class Base {
fun foo() { fun foo() {
} }
} }
@@ -1,4 +1,3 @@
//class
class B { class B {
B(int i) {} B(int i) {}
int call() {return 1;} int call() {return 1;}
@@ -1,5 +1,5 @@
class B(i: Int) { open class B(i: Int) {
fun call(): Int { open fun call(): Int {
return 1 return 1
} }
} }
@@ -1,7 +1,6 @@
// ERROR: This type is final, so it cannot be inherited from
package a.b package a.b
class Base { open class Base {
fun foo() { fun foo() {
} }
} }