Big refactoring in j2k converter to support java&kotlin code updating to reflect changes in declarations produced by conversion (work in progress but all tests pass)

This commit is contained in:
Valentin Kipyatkov
2014-10-17 20:45:06 +04:00
committed by valentin
parent 16f117571f
commit 030faa4630
37 changed files with 876 additions and 573 deletions
+1
View File
@@ -4,6 +4,7 @@
<w>initializers</w>
<w>inserter</w>
<w>pparent</w>
<w>processings</w>
<w>rparenth</w>
</words>
</dictionary>
@@ -25,10 +25,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiJavaFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.j2k.Converter;
import org.jetbrains.jet.j2k.ConverterSettings;
import org.jetbrains.jet.j2k.FilesConversionScope;
import org.jetbrains.jet.j2k.IdeaReferenceSearcher;
import org.jetbrains.jet.j2k.*;
import org.jetbrains.jet.plugin.j2k.J2kPostProcessor;
import java.util.List;
@@ -47,7 +44,7 @@ public class JavaToKotlinAction extends AnAction {
return;
}
final Converter converter = Converter.OBJECT$.create(project,
final JavaToKotlinConverter converter = new JavaToKotlinConverter(project,
ConverterSettings.defaultSettings,
new FilesConversionScope(selectedJavaFiles),
IdeaReferenceSearcher.INSTANCE$,
@@ -27,7 +27,7 @@ import com.intellij.psi.PsiManager;
import com.intellij.psi.codeStyle.CodeStyleManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.j2k.Converter;
import org.jetbrains.jet.j2k.JavaToKotlinConverter;
import java.io.IOException;
import java.util.*;
@@ -83,7 +83,7 @@ public class JavaToKotlinActionUtil {
}
@NotNull
static List<VirtualFile> convertFiles(final Converter converter, List<PsiJavaFile> allJavaFilesNear) {
static List<VirtualFile> convertFiles(final JavaToKotlinConverter converter, List<PsiJavaFile> allJavaFilesNear) {
final List<VirtualFile> result = new LinkedList<VirtualFile>();
for (final PsiFile f : allJavaFilesNear) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@@ -120,7 +120,7 @@ public class JavaToKotlinActionUtil {
}
@Nullable
private static VirtualFile convertOneFile(Converter converter, PsiFile psiFile) {
private static VirtualFile convertOneFile(JavaToKotlinConverter converter, PsiFile psiFile) {
try {
VirtualFile virtualFile = psiFile.getVirtualFile();
if (psiFile instanceof PsiJavaFile && virtualFile != null) {
@@ -86,7 +86,7 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor<TextBl
}
private fun convertCopiedCodeToKotlin(code: CopiedCode, fileCopiedFrom: PsiJavaFile, fileCopiedTo: JetFile): String {
val converter = Converter.create(fileCopiedFrom.getProject(),
val converter = JavaToKotlinConverter(fileCopiedFrom.getProject(),
ConverterSettings.defaultSettings,
FilesConversionScope(listOf(fileCopiedFrom)),
IdeaReferenceSearcher,
@@ -105,7 +105,7 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor<TextBl
private fun convertRangeToKotlin(file: PsiJavaFile,
range: TextRange,
converter: Converter): String {
converter: JavaToKotlinConverter): String {
val result = StringBuilder()
var currentRange = range
//TODO: probably better to use document to get text by range
@@ -31,7 +31,7 @@ class AnnotationConverter(private val converter: Converter) {
private fun convertAnnotationsOnly(owner: PsiModifierListOwner): Annotations {
val modifierList = owner.getModifierList()
val annotations = modifierList?.getAnnotations()?.filter { it.getQualifiedName() !in annotationsToRemove }
if (annotations == null || annotations.isEmpty()) return Annotations.Empty
if (annotations == null || annotations.isEmpty()) return Annotations.Empty()
val newLines = run {
if (!modifierList!!.isInSingleLine()) {
@@ -73,7 +73,7 @@ class AnnotationConverter(private val converter: Converter) {
val nameRef = annotation.getNameReferenceElement()
val name = Identifier((nameRef ?: return null).getText()!!).assignPrototype(nameRef)
val annotationClass = nameRef!!.resolve() as? PsiClass
val annotationClass = nameRef.resolve() as? PsiClass
val lastMethod = annotationClass?.getMethods()?.lastOrNull()
val arguments = annotation.getParameterList().getAttributes().flatMap {
val method = annotationClass?.findMethodsByName(it.getName() ?: "value", false)?.firstOrNull()
@@ -97,7 +97,7 @@ class AnnotationConverter(private val converter: Converter) {
private fun convertAttributeValue(value: PsiAnnotationMemberValue?, expectedType: PsiType?, isVararg: Boolean, isUnnamed: Boolean): List<Expression> {
return when (value) {
is PsiExpression -> listOf(converter.convertExpression(value as? PsiExpression, expectedType).assignPrototype(value))
is PsiExpression -> listOf(converter.createDefaultCodeConverter()/*TODO*/.convertExpression(value as? PsiExpression, expectedType).assignPrototype(value))
is PsiArrayInitializerMemberValue -> {
val componentType = (expectedType as? PsiArrayType)?.getComponentType()
@@ -35,9 +35,15 @@ import com.intellij.psi.PsiExpressionStatement
import com.intellij.psi.PsiAssignmentExpression
import com.intellij.psi.PsiExpression
import com.intellij.psi.JavaTokenType
import com.intellij.psi.PsiElementFactory
class FieldCorrectionInfo(val name: Identifier, val access: Modifier?, val setterAccess: Modifier?)
class FieldCorrectionInfo(val name: String, val access: Modifier?, val setterAccess: Modifier?) {
val identifier = Identifier(name).assignNoPrototype()
}
enum class AccessorKind {
GETTER
SETTER
}
class ClassBodyConverter(private val psiClass: PsiClass,
private val converter: Converter) {
@@ -47,10 +53,8 @@ class ClassBodyConverter(private val psiClass: PsiClass,
public fun convertBody(): ClassBody {
processAccessorsToDrop()
val correctedConverter = buildConverterWithCorrectedFieldNames()
val constructorConverter = if (psiClass.getName() != null)
ConstructorConverter(psiClass, correctedConverter, fieldCorrections)
ConstructorConverter(psiClass, converter, fieldCorrections)
else
null
@@ -59,8 +63,8 @@ class ClassBodyConverter(private val psiClass: PsiClass,
if (element is PsiMember) {
if (element is PsiAnnotationMethod) continue // converted in convertAnnotationType()
val converted = correctedConverter.convertMember(element, membersToRemove, constructorConverter)
if (converted != null && !converted.isEmpty) {
val converted = converter.convertMember(element, membersToRemove, constructorConverter)
if (converted != null/* && !converted.isEmpty()*/) {
convertedMembers.put(element, converted)
}
}
@@ -79,11 +83,8 @@ class ClassBodyConverter(private val psiClass: PsiClass,
for ((psiMember, member) in convertedMembers) {
if (member is PrimaryConstructor) {
assert(primaryConstructorSignature == null)
primaryConstructorSignature = member.signature()
val initializer = member.initializer()
if (initializer != null) {
members.add(initializer)
}
primaryConstructorSignature = member.createSignature(converter)
members.add(member.initializer())
}
else if (member is FactoryFunction) {
factoryFunctions.add(member)
@@ -171,17 +172,10 @@ class ClassBodyConverter(private val psiClass: PsiClass,
else
converter.convertModifiers(field).accessModifier()
//TODO: check that setter access is not bigger
fieldCorrections[field] = FieldCorrectionInfo(Identifier(getterInfo.propertyName).assignNoPrototype(),
getterAccess,
setterAccess)
fieldCorrections[field] = FieldCorrectionInfo(getterInfo.propertyName, getterAccess, setterAccess)
}
}
private enum class AccessorKind {
GETTER
SETTER
}
private class AccessorInfo(val method: PsiMethod, val field: PsiField, val kind: AccessorKind, val propertyName: String)
private fun getAccessorInfo(method: PsiMethod): AccessorInfo? {
@@ -215,36 +209,4 @@ class ClassBodyConverter(private val psiClass: PsiClass,
if (field.getContainingClass() != psiClass || field.hasModifierProperty(PsiModifier.STATIC)) return null
return field
}
//TODO: correct usages to accessors (and field too) across all code being converted + all other Kotlin code in the project
private fun buildConverterWithCorrectedFieldNames(): Converter {
if (fieldCorrections.isEmpty()) return converter
return converter.withExpressionConverter { prevExpressionConverter ->
object : ExpressionConverter {
override fun convertExpression(expression: PsiExpression, converter: Converter): Expression {
val result = prevExpressionConverter.convertExpression(expression, converter)
if (expression !is PsiReferenceExpression) return result
val field = expression.resolve() as? PsiField ?: return result
val correction = fieldCorrections[field] ?: return result
if (correction.name.name == field.getName()) return result
val qualifier = expression.getQualifierExpression()
return if (qualifier != null) {
QualifiedExpression(converter.convertExpression(qualifier), correction.name)
}
else {
// check if field name is shadowed
val elementFactory = PsiElementFactory.SERVICE.getInstance(expression.getProject())
val refExpr = elementFactory.createExpressionFromText(correction.name.name, expression) as PsiReferenceExpression
if (refExpr.resolve() == null)
correction.name
else
QualifiedExpression(ThisExpression(Identifier.Empty).assignNoPrototype(), correction.name) //TODO: this is not correct in case of nested/anonymous classes
}
}
}
}
}
}
@@ -0,0 +1,171 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.j2k
import com.intellij.psi.PsiType
import com.intellij.psi.PsiCodeBlock
import com.intellij.psi.PsiStatement
import org.jetbrains.jet.j2k.ast.Block
import org.jetbrains.jet.j2k.ast.LBrace
import org.jetbrains.jet.j2k.ast.assignPrototype
import org.jetbrains.jet.j2k.ast.RBrace
import org.jetbrains.jet.j2k.ast.Statement
import com.intellij.psi.PsiExpression
import org.jetbrains.jet.j2k.ast.Expression
import com.intellij.psi.PsiLocalVariable
import org.jetbrains.jet.j2k.ast.LocalVariable
import com.intellij.psi.PsiModifier
import org.jetbrains.jet.j2k.ast.declarationIdentifier
import org.jetbrains.jet.j2k.ast.Identifier
import com.intellij.psi.PsiPrimitiveType
import com.intellij.psi.PsiClassType
import org.jetbrains.jet.j2k.ast.BangBangExpression
import org.jetbrains.jet.j2k.ast.assignNoPrototype
import org.jetbrains.jet.j2k.ast.LiteralExpression
import org.jetbrains.jet.j2k.ast.MethodCallExpression
import org.jetbrains.jet.j2k.ast.Type
import org.jetbrains.jet.j2k.ast.ErrorType
import org.jetbrains.jet.j2k.ast.Nullability
import com.intellij.psi.CommonClassNames.JAVA_LANG_BYTE
import com.intellij.psi.CommonClassNames.JAVA_LANG_SHORT
import com.intellij.psi.CommonClassNames.JAVA_LANG_INTEGER
import com.intellij.psi.CommonClassNames.JAVA_LANG_LONG
import com.intellij.psi.CommonClassNames.JAVA_LANG_FLOAT
import com.intellij.psi.CommonClassNames.JAVA_LANG_DOUBLE
import com.intellij.psi.CommonClassNames.JAVA_LANG_CHARACTER
import com.intellij.psi.PsiAnonymousClass
import org.jetbrains.jet.j2k.ast.AnonymousClassBody
class CodeConverter(public val converter: Converter,
private val expressionConverter: ExpressionConverter,
private val statementConverter: StatementConverter,
public val methodReturnType: PsiType?) {
public val typeConverter: TypeConverter = converter.typeConverter
public val settings: ConverterSettings = converter.settings
public fun withSpecialExpressionConverter(specialConverter: SpecialExpressionConverter): CodeConverter
= CodeConverter(converter, expressionConverter.withSpecialConverter(specialConverter), statementConverter, methodReturnType)
public fun withMethodReturnType(methodReturnType: PsiType?): CodeConverter
= CodeConverter(converter, expressionConverter, statementConverter, methodReturnType)
public fun convertBlock(block: PsiCodeBlock?, notEmpty: Boolean = true, statementFilter: (PsiStatement) -> Boolean = { true }): Block {
if (block == null) return Block.Empty()
val lBrace = LBrace().assignPrototype(block.getLBrace())
val rBrace = RBrace().assignPrototype(block.getRBrace())
return Block(block.getStatements().filter(statementFilter).map { convertStatement(it) }, lBrace, rBrace, notEmpty).assignPrototype(block)
}
public fun convertStatement(statement: PsiStatement?): Statement {
if (statement == null) return Statement.Empty
return statementConverter.convertStatement(statement, this).assignPrototype(statement)
}
public fun convertExpressions(expressions: Array<PsiExpression>): List<Expression>
= expressions.map { convertExpression(it) }
public fun convertExpression(expression: PsiExpression?): Expression {
if (expression == null) return Expression.Empty
return expressionConverter.convertExpression(expression, this).assignPrototype(expression)
}
public fun convertLocalVariable(variable: PsiLocalVariable): LocalVariable {
val isVal = variable.hasModifierProperty(PsiModifier.FINAL) ||
variable.getInitializer() == null/* we do not know actually and prefer val until we have better analysis*/ ||
!variable.hasWriteAccesses(converter.referenceSearcher, variable.getContainingMethod())
return LocalVariable(variable.declarationIdentifier(),
converter.convertAnnotations(variable),
converter.convertModifiers(variable),
converter.variableTypeToDeclare(variable, settings.specifyLocalVariableTypeByDefault, isVal),
convertExpression(variable.getInitializer(), variable.getType()),
isVal).assignPrototype(variable)
}
public fun convertExpression(expression: PsiExpression?, expectedType: PsiType?): Expression {
if (expression == null) return Identifier.Empty()
var convertedExpression = convertExpression(expression)
if (expectedType == null || expectedType == PsiType.VOID) return convertedExpression
val actualType = expression.getType()
if (actualType == null) return convertedExpression
if (convertedExpression.isNullable &&
(actualType is PsiPrimitiveType || actualType is PsiClassType && expectedType is PsiPrimitiveType)) {
convertedExpression = BangBangExpression(convertedExpression).assignNoPrototype()
}
if (needConversion(actualType, expectedType) && convertedExpression !is LiteralExpression) {
val conversion = PRIMITIVE_TYPE_CONVERSIONS[expectedType.getCanonicalText()]
if (conversion != null) {
convertedExpression = MethodCallExpression.buildNotNull(convertedExpression, conversion)
}
}
return convertedExpression.assignPrototype(expression)
}
public fun convertedExpressionType(expression: PsiExpression, expectedType: PsiType): Type {
var convertedExpression = convertExpression(expression)
val actualType = expression.getType()
if (actualType == null) return ErrorType()
var resultType = typeConverter.convertType(actualType, if (convertedExpression.isNullable) Nullability.Nullable else Nullability.NotNull)
if (actualType is PsiPrimitiveType && resultType.isNullable ||
expectedType is PsiPrimitiveType && actualType is PsiClassType) {
resultType = resultType.toNotNullType()
}
if (needConversion(actualType, expectedType) && convertedExpression !is LiteralExpression) {
val conversion = PRIMITIVE_TYPE_CONVERSIONS[expectedType.getCanonicalText()]
if (conversion != null) {
resultType = typeConverter.convertType(expectedType, Nullability.NotNull)
}
}
return resultType
}
public fun convertAnonymousClassBody(anonymousClass: PsiAnonymousClass): AnonymousClassBody {
return AnonymousClassBody(ClassBodyConverter(anonymousClass, converter).convertBody(),
anonymousClass.getBaseClassType().resolve()?.isInterface() ?: false).assignPrototype(anonymousClass)
}
private fun needConversion(actual: PsiType, expected: PsiType): Boolean {
val expectedStr = expected.getCanonicalText()
val actualStr = actual.getCanonicalText()
return expectedStr != actualStr &&
expectedStr != typeConversionMap[actualStr] &&
actualStr != typeConversionMap[expectedStr]
}
private val typeConversionMap: Map<String, String> = mapOf(
JAVA_LANG_BYTE to "byte",
JAVA_LANG_SHORT to "short",
JAVA_LANG_INTEGER to "int",
JAVA_LANG_LONG to "long",
JAVA_LANG_FLOAT to "float",
JAVA_LANG_DOUBLE to "double",
JAVA_LANG_CHARACTER to "char"
)
}
@@ -164,33 +164,36 @@ class ConstructorConverter(private val psiClass: PsiClass,
if (constructor in constructorsToDrop) return null
val params = converter.convertParameterList(constructor.getParameterList())
val bodyConverter = converter.withExpressionConverter { prevExpressionConverter ->
object : ExpressionConverter {
override fun convertExpression(expression: PsiExpression, converter: Converter): Expression {
if (expression is PsiReferenceExpression && expression.isQualifierEmptyOrThis()) {
val member = expression.getReference()?.resolve() as? PsiMember
if (member != null &&
!member.isConstructor() &&
member.getContainingClass() == constructor.getContainingClass()) {
val isNullable = member is PsiField && typeConverter.variableNullability(member).isNullable(converter.settings)
val qualifier = if (member.hasModifierProperty(PsiModifier.STATIC)) constructor.declarationIdentifier() else tempValIdentifier()
val name = fieldCorrections[member]?.name ?: Identifier(expression.getReferenceName()!!, isNullable).assignNoPrototype()
return QualifiedExpression(qualifier, name)
}
}
return prevExpressionConverter.convertExpression(expression, converter)
}
}
}
var body = postProcessBody(bodyConverter.convertBlock(constructor.getBody()))
val containingClass = constructor.getContainingClass()
val typeParameterList = converter.convertTypeParameterList(containingClass?.getTypeParameterList())
val factoryFunctionType = ClassType(ReferenceElement(containingClass?.declarationIdentifier() ?: Identifier.Empty, typeParameterList.parameters).assignNoPrototype(),
val factoryFunctionType = ClassType(ReferenceElement(containingClass?.declarationIdentifier() ?: Identifier.Empty(), typeParameterList.parameters).assignNoPrototype(),
Nullability.NotNull,
converter.settings).assignNoPrototype()
fun convertBody(codeConverter: CodeConverter): Block {
val bodyConverter = codeConverter.withSpecialExpressionConverter(
object : SpecialExpressionConverter {
override fun convertExpression(expression: PsiExpression, codeConverter: CodeConverter): Expression? {
if (expression is PsiReferenceExpression && expression.isQualifierEmptyOrThis()) {
val member = expression.getReference()?.resolve() as? PsiMember
if (member != null &&
!member.isConstructor() &&
member.getContainingClass() == constructor.getContainingClass()) {
val isNullable = member is PsiField && typeConverter.variableNullability(member).isNullable(codeConverter.settings)
val qualifier = if (member.hasModifierProperty(PsiModifier.STATIC)) constructor.declarationIdentifier() else tempValIdentifier()
val name = fieldCorrections[member]?.name ?: expression.getReferenceName()!!
return QualifiedExpression(qualifier, Identifier(name, isNullable).assignNoPrototype())
}
}
return null
}
})
return postProcessBody(bodyConverter.convertBlock(constructor.getBody()))
}
return FactoryFunction(constructor.declarationIdentifier(), annotations, correctFactoryFunctionAccess(modifiers),
factoryFunctionType, params, typeParameterList, body)
factoryFunctionType, params, typeParameterList, converter.lazyElement(::convertBody))
}
}
@@ -204,7 +207,8 @@ class ConstructorConverter(private val psiClass: PsiClass,
val parameterUsageReplacementMap = HashMap<String, String>()
val correctedTypeConverter = converter.withSpecialContext(psiClass).typeConverter /* to correct nested class references */
val block = if (body != null) {
val bodyGenerator: (CodeConverter) -> Block = if (body != null) {
val statementsToRemove = HashSet<PsiStatement>()
for (parameter in params) {
val (field, initializationStatement) = findBackingFieldForConstructorParameter(parameter, primaryConstructor) ?: continue
@@ -230,44 +234,46 @@ class ConstructorConverter(private val psiClass: PsiClass,
statementsToRemove.add(initializationStatement)
membersToRemove.add(field)
val fieldName = fieldCorrection?.name?.name ?: field.getName()!!
val fieldName = fieldCorrection?.name ?: field.getName()!!
if (fieldName != parameter.getName()) {
parameterUsageReplacementMap.put(parameter.getName()!!, fieldName)
}
}
val bodyConverter = converter.withExpressionConverter { prevExpressionConverter ->
object : ReplacingExpressionConverter(parameterUsageReplacementMap, prevExpressionConverter) {
override fun convertExpression(expression: PsiExpression, converter: Converter): Expression {
if (expression is PsiMethodCallExpression && expression.isSuperConstructorCall()) {
return Expression.Empty // skip it
}
return super.convertExpression(expression, converter)
}
}
{ codeConverter ->
val bodyConverter = codeConverter.withSpecialExpressionConverter(
object : ReplacingExpressionConverter(this, parameterUsageReplacementMap) {
override fun convertExpression(expression: PsiExpression, codeConverter: CodeConverter): Expression? {
if (expression is PsiMethodCallExpression && expression.isSuperConstructorCall()) {
return Expression.Empty // skip it
}
return super.convertExpression(expression, codeConverter)
}
})
postProcessBody(bodyConverter.convertBlock(body, false, { !statementsToRemove.contains(it) }))
}
postProcessBody(bodyConverter.convertBlock(body, false, { !statementsToRemove.contains(it) }))
}
else {
Block.Empty
{ it -> Block.Empty() }
}
// we need to replace renamed parameter usages in base class constructor arguments and in default values
val correctedConverter = converter.withExpressionConverter {
prevExpressionConverter -> ReplacingExpressionConverter(parameterUsageReplacementMap, prevExpressionConverter)
}.withSpecialContext(psiClass) /* to correct nested class references */
val correctedConverter = converter.withSpecialContext(psiClass) /* to correct nested class references */
val correctedCodeConverter = correctedConverter.createDefaultCodeConverter()/*TODO!!!*/
.withSpecialExpressionConverter(ReplacingExpressionConverter(this, parameterUsageReplacementMap))
val statement = primaryConstructor.getBody()?.getStatements()?.firstOrNull()
val methodCall = (statement as? PsiExpressionStatement)?.getExpression() as? PsiMethodCallExpression
if (methodCall != null && methodCall.isSuperConstructorCall()) {
baseClassParams = correctedConverter.convertExpressions(methodCall.getArgumentList().getExpressions())
baseClassParams = correctedCodeConverter.convertExpressions(methodCall.getArgumentList().getExpressions())
}
val parameterList = ParameterList(params.indices.map { i ->
val parameter = params[i]
val indexFromEnd = params.size - i - 1
val defaultValue = if (indexFromEnd < lastParamDefaults.size)
correctedConverter.convertExpression(lastParamDefaults[indexFromEnd], parameter.getType())
correctedCodeConverter.convertExpression(lastParamDefaults[indexFromEnd], parameter.getType())
else
null
if (!parameterToField.containsKey(parameter)) {
@@ -276,7 +282,7 @@ class ConstructorConverter(private val psiClass: PsiClass,
else {
val (field, type) = parameterToField[parameter]!!
val fieldCorrection = fieldCorrections[field]
val name = fieldCorrection?.name ?: field.declarationIdentifier()
val name = fieldCorrection?.identifier ?: field.declarationIdentifier()
val accessModifiers = if (fieldCorrection != null)
Modifiers(listOf()).with(fieldCorrection.access).assignNoPrototype()
else
@@ -289,7 +295,7 @@ class ConstructorConverter(private val psiClass: PsiClass,
defaultValue).assignPrototypes(listOf(parameter, field), CommentsAndSpacesInheritance(blankLinesBefore = false))
}
}).assignPrototype(primaryConstructor.getParameterList())
return PrimaryConstructor(annotations, modifiers, parameterList, block).assignPrototype(primaryConstructor)
return PrimaryConstructor(annotations, modifiers, parameterList, converter.lazyElement(bodyGenerator)).assignPrototype(primaryConstructor)
}
private fun findBackingFieldForConstructorParameter(parameter: PsiParameter, constructor: PsiMethod): Pair<PsiField, PsiStatement>? {
@@ -328,8 +334,8 @@ class ConstructorConverter(private val psiClass: PsiClass,
return generateArtificialPrimaryConstructor(classBody)
}
else {
val updatedFunctions = processFactoryFunctionsWithConstructorCall(classBody.factoryFunctions)
return ClassBody(classBody.primaryConstructorSignature, classBody.baseClassParams, classBody.members, classBody.classObjectMembers, updatedFunctions, classBody.lBrace, classBody.rBrace)
processFactoryFunctionsWithConstructorCall(classBody.factoryFunctions)
return classBody
}
}
@@ -337,14 +343,13 @@ class ConstructorConverter(private val psiClass: PsiClass,
assert(classBody.primaryConstructorSignature == null)
val fieldsToInitialize = classBody.members.filterIsInstance(javaClass<Field>()).filter { it.isVal }
val updatedFactoryFunctions = ArrayList<FactoryFunction>()
for (function in classBody.factoryFunctions) {
val body = function.body!!
// 2 cases: secondary constructor either calls another constructor or does not call any
val newStatements = processFactoryFunctionWithConstructorCall(body) ?:
insertCallToArtificialPrimary(body, fieldsToInitialize)
val newBody = Block(newStatements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
updatedFactoryFunctions.add(function.withBody(newBody))
function.body!!.updateGenerator { (codeConverter, block) ->
// 2 cases: secondary constructor either calls another constructor or does not call any
val newStatements = processFactoryFunctionWithConstructorCall(block) ?:
insertCallToArtificialPrimary(block, fieldsToInitialize)
Block(newStatements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
}
}
val parameters = fieldsToInitialize.map { field ->
@@ -352,24 +357,25 @@ class ConstructorConverter(private val psiClass: PsiClass,
Parameter(field.identifier, field.type, varValModifier, field.annotations, field.modifiers.filter { it in ACCESS_MODIFIERS }).assignPrototypesFrom(field)
}
val modifiers = Modifiers.Empty
val modifiers = Modifiers.Empty()
//TODO: we can generate it private when secondary constructors are supported by Kotlin
//val modifiers = Modifiers(listOf(Modifier.PRIVATE)).assignNoPrototype()
val parameterList = ParameterList(parameters).assignNoPrototype()
val constructorSignature = PrimaryConstructorSignature(Annotations.Empty, modifiers, parameterList).assignNoPrototype()
val constructorSignature = PrimaryConstructorSignature(Annotations.Empty(), modifiers, parameterList).assignNoPrototype()
val updatedMembers = classBody.members.filter { !fieldsToInitialize.contains(it) }
return ClassBody(constructorSignature, classBody.baseClassParams, updatedMembers, classBody.classObjectMembers, updatedFactoryFunctions, classBody.lBrace, classBody.rBrace)
return ClassBody(constructorSignature, classBody.baseClassParams, updatedMembers, classBody.classObjectMembers, classBody.factoryFunctions, classBody.lBrace, classBody.rBrace)
}
private fun processFactoryFunctionsWithConstructorCall(functions: List<FactoryFunction>): List<FactoryFunction> {
return functions.map { function ->
val body = function.body!!
val statements = processFactoryFunctionWithConstructorCall(body)
if (statements != null) {
function.withBody(Block(statements, body.lBrace, body.rBrace).assignPrototypesFrom(body))
}
else {
function
private fun processFactoryFunctionsWithConstructorCall(functions: List<FactoryFunction>) {
for (function in functions) {
function.body!!.updateGenerator { (codeConverter, block) ->
val statements = processFactoryFunctionWithConstructorCall(block)
if (statements != null) {
Block(statements, block.lBrace, block.rBrace).assignPrototypesFrom(block)
}
else {
block
}
}
}
}
@@ -387,7 +393,7 @@ class ConstructorConverter(private val psiClass: PsiClass,
statements[i] = ReturnStatement(constructorCall).assignNoPrototype()
return statements
}
val localVar = LocalVariable(tempValIdentifier(), Annotations.Empty, Modifiers.Empty, null, constructorCall, true).assignNoPrototype()
val localVar = LocalVariable(tempValIdentifier(), Annotations.Empty(), Modifiers.Empty(), null, constructorCall, true).assignNoPrototype()
statements[i] = DeclarationStatement(listOf(localVar)).assignNoPrototype()
statements.add(ReturnStatement(tempValIdentifier()).assignNoPrototype())
return statements
@@ -431,8 +437,8 @@ class ConstructorConverter(private val psiClass: PsiClass,
val initializer = MethodCallExpression.buildNotNull(null, className, arguments).assignNoPrototype()
if (statements.isNotEmpty()) {
val localVar = LocalVariable(tempValIdentifier(),
Annotations.Empty,
Modifiers.Empty,
Annotations.Empty(),
Modifiers.Empty(),
null,
initializer,
true).assignNoPrototype()
@@ -460,11 +466,10 @@ class ConstructorConverter(private val psiClass: PsiClass,
return ref.getCanonicalText() == "super" && ref.resolve()?.isConstructor() ?: false
}
private inner open class ReplacingExpressionConverter(
val parameterUsageReplacementMap: Map<String, String>,
val prevExpressionConverter: ExpressionConverter
) : ExpressionConverter {
override fun convertExpression(expression: PsiExpression, converter: Converter): Expression {
private /*inner*//*TODO: see KT-5343*/ open class ReplacingExpressionConverter(
val owner: ConstructorConverter, val parameterUsageReplacementMap: Map<String, String>) : SpecialExpressionConverter {
override fun convertExpression(expression: PsiExpression, codeConverter: CodeConverter): Expression? {
if (expression is PsiReferenceExpression && expression.getQualifier() == null) {
val replacement = parameterUsageReplacementMap[expression.getReferenceName()]
if (replacement != null) {
@@ -472,14 +477,14 @@ class ConstructorConverter(private val psiClass: PsiClass,
if (target is PsiParameter) {
val scope = target.getDeclarationScope()
// we do not check for exactly this constructor because default values reference parameters in other constructors
if (scope.isConstructor() && scope.getParent() == psiClass) {
return Identifier(replacement, converter.typeConverter.variableNullability(target).isNullable(converter.settings))
if (scope.isConstructor() && scope.getParent() == owner.psiClass) {
return Identifier(replacement, codeConverter.typeConverter.variableNullability(target).isNullable(codeConverter.settings))
}
}
}
}
return prevExpressionConverter.convertExpression(expression, converter)
return null
}
}
}
+108 -249
View File
@@ -24,122 +24,54 @@ import org.jetbrains.jet.lang.types.expressions.OperatorConventions.*
import com.intellij.openapi.project.Project
import com.intellij.psi.util.PsiMethodUtil
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.lang.resolve.BindingContext
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProcessCanceledException
import org.jetbrains.jet.j2k.usageProcessing.UsageProcessing
import org.jetbrains.jet.j2k.usageProcessing.FieldToPropertyProcessing
public trait ConversionScope {
public fun contains(element: PsiElement): Boolean
}
class Converter private(private val elementToConvert: PsiElement,
val settings: ConverterSettings,
val conversionScope: ConversionScope,
val referenceSearcher: ReferenceSearcher,
private val postProcessor: PostProcessor?,
private val state: Converter.State) {
public class FilesConversionScope(val files: Collection<PsiJavaFile>) : ConversionScope {
override fun contains(element: PsiElement) = files.any { element.getContainingFile() == it }
}
private class State(val specialContext: PsiElement?,
val importsToAdd: MutableSet<String>,
val lazyElements: MutableList<LazyElement<*>>,
val usageProcessings: MutableCollection<UsageProcessing>,
val postUnfoldActions: MutableList<() -> Unit>)
public trait PostProcessor {
public val contextToAnalyzeIn: PsiElement
public fun analyzeFile(file: JetFile): BindingContext
public fun doAdditionalProcessing(file: JetFile)
}
public val project: Project = elementToConvert.getProject()
public val typeConverter: TypeConverter = TypeConverter(this)
public val annotationConverter: AnnotationConverter = AnnotationConverter(this)
public class Converter private(val project: Project,
val settings: ConverterSettings,
val conversionScope: ConversionScope,
val referenceSearcher: ReferenceSearcher,
private val postProcessor: PostProcessor?,
private val state: Converter.State) {
private class State(val methodReturnType: PsiType?,
val expressionConverter: ExpressionConverter,
val statementConverter: StatementConverter,
val specialContext: PsiElement?,
val importList: ImportList?,
val importsToAdd: MutableCollection<String>?)
public val specialContext: PsiElement? = state.specialContext
public val importsToAdd: MutableCollection<String> = state.importsToAdd
val typeConverter: TypeConverter = TypeConverter(this)
val methodReturnType: PsiType? = state.methodReturnType
val specialContext: PsiElement? = state.specialContext
val importNames: Set<String> = state.importList?.imports?.mapTo(HashSet<String>()) { it.name } ?: setOf()
val importsToAdd: MutableCollection<String>? = state.importsToAdd
val annotationConverter = AnnotationConverter(this)
private val importList = (elementToConvert as? PsiJavaFile)?.getImportList()?.let { convertImportList(it) }
public val importNames: Set<String> = importList?.imports?.mapTo(HashSet<String>()) { it.name } ?: setOf()
class object {
private val LOG = Logger.getInstance("#org.jetbrains.jet.j2k.Converter")
public fun create(project: Project, settings: ConverterSettings, conversionScope: ConversionScope,
public fun create(elementToConvert: PsiElement, settings: ConverterSettings, conversionScope: ConversionScope,
referenceSearcher: ReferenceSearcher, postProcessor: PostProcessor?): Converter {
val state = State(null, DefaultExpressionConverter(), DefaultStatementConverter(), null, null, null)
return Converter(project, settings, conversionScope, referenceSearcher, postProcessor, state)
return Converter(elementToConvert, settings, conversionScope, referenceSearcher, postProcessor, State(null, LinkedHashSet(), ArrayList(), ArrayList(), ArrayList()))
}
}
fun withMethodReturnType(methodReturnType: PsiType?): Converter
= Converter(project, settings, conversionScope, referenceSearcher, postProcessor,
State(methodReturnType, state.expressionConverter, state.statementConverter, state.specialContext, state.importList, state.importsToAdd))
public fun withSpecialContext(context: PsiElement): Converter
= Converter(elementToConvert, settings, conversionScope, referenceSearcher, postProcessor,
State(context, state.importsToAdd, state.lazyElements, state.usageProcessings, state.postUnfoldActions))
fun withExpressionConverter(factory: (prevConverter: ExpressionConverter) -> ExpressionConverter): Converter
= Converter(project, settings, conversionScope, referenceSearcher, postProcessor,
State(state.methodReturnType, factory(state.expressionConverter), state.statementConverter, state.specialContext, state.importList, state.importsToAdd))
/*TODO: it should be private*/ fun createDefaultCodeConverter() = CodeConverter(this, DefaultExpressionConverter(), DefaultStatementConverter(), null)
fun withStatementConverter(factory: (prevConverter: StatementConverter) -> StatementConverter): Converter
= Converter(project, settings, conversionScope, referenceSearcher, postProcessor,
State(state.methodReturnType, state.expressionConverter, factory(state.statementConverter), state.specialContext, state.importList, state.importsToAdd))
public fun convert(): Element? = convertTopElement(elementToConvert)
fun withSpecialContext(context: PsiElement): Converter
= Converter(project, settings, conversionScope, referenceSearcher, postProcessor,
State(state.methodReturnType, state.expressionConverter, state.statementConverter, context, state.importList, state.importsToAdd))
private fun withImportList(importList: ImportList): Converter
= Converter(project, settings, conversionScope, referenceSearcher, postProcessor,
State(state.methodReturnType, state.expressionConverter, state.statementConverter, state.specialContext, importList, state.importsToAdd))
private fun withImportsToAdd(importsToAdd: MutableCollection<String>): Converter
= Converter(project, settings, conversionScope, referenceSearcher, postProcessor,
State(state.methodReturnType, state.expressionConverter, state.statementConverter, state.specialContext, state.importList, importsToAdd))
public fun elementToKotlin(element: PsiElement): String {
try {
val converted = convertTopElement(element) ?: return ""
val builder = CodeBuilder(element)
builder.append(converted)
if (postProcessor != null) {
try {
return AfterConversionPass(project, postProcessor).run(builder.result)
}
catch(e: ProcessCanceledException) {
throw e
}
catch(t: Throwable) {
LOG.error(t)
return builder.result
}
}
else {
return builder.result
}
}
catch(e: ElementCreationStackTraceRequiredException) {
// if we got this exception then we need to turn element creation stack traces on to get better diagnostic
Element.saveCreationStacktraces = true
try {
return elementToKotlin(element)
}
finally {
Element.saveCreationStacktraces = false
}
}
}
private fun convertTopElement(element: PsiElement?): Element? = when (element) {
private fun convertTopElement(element: PsiElement): Element? = when (element) {
is PsiJavaFile -> convertFile(element)
is PsiClass -> convertClass(element)
is PsiMethod -> convertMethod(element, null, null)
is PsiField -> convertField(element, null)
is PsiStatement -> convertStatement(element)
is PsiExpression -> convertExpression(element)
is PsiStatement -> createDefaultCodeConverter().convertStatement(element)
is PsiExpression -> createDefaultCodeConverter().convertExpression(element)
is PsiImportList -> convertImportList(element)
is PsiImportStatementBase -> convertImport(element, false)
is PsiAnnotation -> annotationConverter.convertAnnotation(element, false, false)
@@ -147,24 +79,49 @@ public class Converter private(val project: Project,
else -> null
}
public val usageProcessings: Collection<UsageProcessing>
get() = state.usageProcessings
public fun unfoldLazyElements(codeConverter: CodeConverter) {
// we use loop with index because new lazy elements can be added during unfolding
var i = 0
while (i < state.lazyElements.size) {
state.lazyElements[i++].unfold(codeConverter)
}
state.postUnfoldActions.forEach { it() }
}
public fun<TResult : Element> lazyElement(generator: (CodeConverter) -> TResult): LazyElement<TResult> {
val element = LazyElement(generator)
state.lazyElements.add(element)
return element
}
public fun addUsageProcessing(processing: UsageProcessing) {
state.usageProcessings.add(processing)
}
public fun addPostUnfoldLazyElementsAction(action: () -> Unit) {
state.postUnfoldActions.add(action)
}
private fun convertFile(javaFile: PsiJavaFile): File {
val importsToAdd = LinkedHashSet<String>()
var converter = this.withImportsToAdd(importsToAdd)
var convertedChildren = javaFile.getChildren().map {
if (it is PsiImportList) {
val importList = convertImportList(it)
converter = converter.withImportList(importList)
assert(it == javaFile.getImportList())
importList
}
else {
converter.convertTopElement(it)
convertTopElement(it)
}
}.filterNotNull()
if (importsToAdd.isNotEmpty()) {
val importList = convertedChildren.filterIsInstance(javaClass<ImportList>()).first()
val newImportList = ImportList(importList.imports + importsToAdd.map { Import(it).assignNoPrototype() }).assignPrototypesFrom(importList)
convertedChildren = convertedChildren.map { if (it == importList) newImportList else it }
addPostUnfoldLazyElementsAction {
assert(importList != null)
if (importsToAdd.isNotEmpty()) {
importList!!.imports = importList.imports + importsToAdd.map { Import(it).assignNoPrototype() }
}
}
return File(convertedChildren, createMainFunction(javaFile)).assignPrototype(javaFile)
@@ -180,20 +137,15 @@ public class Converter private(val project: Project,
return null
}
fun convertAnonymousClassBody(anonymousClass: PsiAnonymousClass): AnonymousClassBody {
return AnonymousClassBody(ClassBodyConverter(anonymousClass, this).convertBody(),
anonymousClass.getBaseClassType().resolve()?.isInterface() ?: false).assignPrototype(anonymousClass)
}
fun convertAnnotations(owner: PsiModifierListOwner): Annotations
public fun convertAnnotations(owner: PsiModifierListOwner): Annotations
= annotationConverter.convertAnnotations(owner)
fun convertClass(psiClass: PsiClass): Class {
public fun convertClass(psiClass: PsiClass): Class {
if (psiClass.isAnnotationType()) {
return convertAnnotationType(psiClass)
}
val annotations = annotationConverter.convertAnnotations(psiClass)
val annotations = convertAnnotations(psiClass)
var modifiers = convertModifiers(psiClass)
val typeParameters = convertTypeParameterList(psiClass.getTypeParameterList())
val implementsTypes = convertToNotNullableTypes(psiClass.getImplementsListTypes())
@@ -242,7 +194,7 @@ public class Converter private(val project: Project,
annotationConverter.convertAnnotationMethodDefault(method)).assignPrototype(method, noBlankLinesInheritance)
}
val parameterList = ParameterList(parameters).assignNoPrototype()
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
var classBody = ClassBodyConverter(psiClass, this).convertBody()
@@ -252,54 +204,58 @@ public class Converter private(val project: Project,
return Class(psiClass.declarationIdentifier(),
(convertAnnotations(psiClass) + Annotations(listOf(annotationAnnotation))).assignNoPrototype(),
convertModifiers(psiClass).without(Modifier.ABSTRACT),
TypeParameterList.Empty,
TypeParameterList.Empty(),
listOf(),
listOf(),
listOf(),
classBody).assignPrototype(psiClass)
}
fun convertInitializer(initializer: PsiClassInitializer): Initializer {
return Initializer(convertBlock(initializer.getBody()), convertModifiers(initializer)).assignPrototype(initializer)
public fun convertInitializer(initializer: PsiClassInitializer): Initializer {
return Initializer(lazyElement { codeConverter -> codeConverter.convertBlock(initializer.getBody()) },
convertModifiers(initializer)).assignPrototype(initializer)
}
fun convertField(field: PsiField, correction: FieldCorrectionInfo?): Member {
val annotations = annotationConverter.convertAnnotations(field)
public fun convertField(field: PsiField, correction: FieldCorrectionInfo?): Member {
val annotations = convertAnnotations(field)
var modifiers = convertModifiers(field)
if (correction != null) {
modifiers = modifiers.without(modifiers.accessModifier()).with(correction.access)
}
val name = correction?.name ?: field.declarationIdentifier()
val name = correction?.identifier ?: field.declarationIdentifier()
val converted = if (field is PsiEnumConstant) {
val argumentList = field.getArgumentList()
EnumConstant(name,
annotations,
modifiers,
typeConverter.convertType(field.getType(), Nullability.NotNull),
ExpressionList(convertExpressions(argumentList?.getExpressions() ?: array())).assignPrototype(argumentList))
lazyElement { codeConverter -> ExpressionList(codeConverter.convertExpressions(argumentList?.getExpressions() ?: array())).assignPrototype(argumentList) })
}
else {
val isVal = isVal(referenceSearcher, field)
val typeToDeclare = variableTypeToDeclare(field,
settings.specifyFieldTypeByDefault || modifiers.isPublic || modifiers.isProtected,
isVal && modifiers.isPrivate)
val initializer = convertExpression(field.getInitializer(), field.getType())
val propertyType = typeToDeclare ?: typeConverter.convertVariableType(field)
addUsageProcessing(FieldToPropertyProcessing(field, correction?.name ?: field.getName(), propertyType.isNullable))
Field(name,
annotations,
modifiers,
typeToDeclare ?: typeConverter.convertVariableType(field),
initializer,
propertyType,
lazyElement { codeConverter -> codeConverter.convertExpression(field.getInitializer(), field.getType()) },
isVal,
typeToDeclare != null,
initializer.isEmpty && shouldGenerateDefaultInitializer(referenceSearcher, field),
shouldGenerateDefaultInitializer(referenceSearcher, field),
if (correction != null) correction.setterAccess else modifiers.accessModifier())
}
return converted.assignPrototype(field)
}
private fun variableTypeToDeclare(variable: PsiVariable, specifyAlways: Boolean, canChangeType: Boolean): Type? {
public fun variableTypeToDeclare(variable: PsiVariable, specifyAlways: Boolean, canChangeType: Boolean): Type? {
fun convertType() = typeConverter.convertVariableType(variable)
if (specifyAlways) return convertType()
@@ -311,27 +267,23 @@ public class Converter private(val project: Project,
if (canChangeType) return null
val convertedType = convertType()
var initializerType = convertedExpressionType(initializer, variable.getType())
var initializerType = createDefaultCodeConverter().convertedExpressionType(initializer, variable.getType())
if (initializerType is ErrorType) return null // do not add explicit type when initializer is not resolved, let user add it if really needed
return if (convertedType == initializerType) null else convertedType
}
fun convertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>?, constructorConverter: ConstructorConverter?): Member? {
return withMethodReturnType(method.getReturnType()).doConvertMethod(method, membersToRemove, constructorConverter)?.assignPrototype(method)
}
private fun doConvertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>?, constructorConverter: ConstructorConverter?): Member? {
public fun convertMethod(method: PsiMethod, membersToRemove: MutableSet<PsiMember>?, constructorConverter: ConstructorConverter?): Member? {
val returnType = typeConverter.convertMethodReturnType(method)
val annotations = (annotationConverter.convertAnnotations(method) + convertThrows(method)).assignNoPrototype()
val annotations = (convertAnnotations(method) + convertThrows(method)).assignNoPrototype()
var modifiers = convertModifiers(method)
val statementsToInsert = ArrayList<Statement>()
for (parameter in method.getParameterList().getParameters()) {
if (parameter.hasWriteAccesses(referenceSearcher, method)) {
val variable = LocalVariable(parameter.declarationIdentifier(),
Annotations.Empty,
Modifiers.Empty,
Annotations.Empty(),
Modifiers.Empty(),
null,
parameter.declarationIdentifier(),
false).assignNoPrototype()
@@ -347,8 +299,8 @@ public class Converter private(val project: Project,
}
}
if (method.isConstructor() && constructorConverter != null) {
return constructorConverter.convertConstructor(method, annotations, modifiers, membersToRemove!!, postProcessBody)
val function = if (method.isConstructor() && constructorConverter != null) {
constructorConverter.convertConstructor(method, annotations, modifiers, membersToRemove!!, postProcessBody)
}
else {
val isOverride = isOverride(method)
@@ -368,9 +320,14 @@ public class Converter private(val project: Project,
var params = convertParameterList(method.getParameterList())
val typeParameterList = convertTypeParameterList(method.getTypeParameterList())
var body = postProcessBody(convertBlock(method.getBody()))
return Function(method.declarationIdentifier(), annotations, modifiers, returnType, typeParameterList, params, body, containingClass?.isInterface() ?: false)
var body = lazyElement { (codeConverter: CodeConverter) ->
val body = codeConverter.withMethodReturnType(method.getReturnType()).convertBlock(method.getBody())
postProcessBody(body)
}
Function(method.declarationIdentifier(), annotations, modifiers, returnType, typeParameterList, params, body, containingClass?.isInterface() ?: false)
}
return function?.assignPrototype(method)
}
/**
@@ -408,42 +365,7 @@ public class Converter private(val project: Project,
return false
}
fun convertBlock(block: PsiCodeBlock?, notEmpty: Boolean = true, statementFilter: (PsiStatement) -> Boolean = { true }): Block {
if (block == null) return Block.Empty
val lBrace = LBrace().assignPrototype(block.getLBrace())
val rBrace = RBrace().assignPrototype(block.getRBrace())
return Block(block.getStatements().filter(statementFilter).map { convertStatement(it) }, lBrace, rBrace, notEmpty).assignPrototype(block)
}
fun convertStatement(statement: PsiStatement?): Statement {
if (statement == null) return Statement.Empty
return state.statementConverter.convertStatement(statement, this).assignPrototype(statement)
}
fun convertExpressions(expressions: Array<PsiExpression>): List<Expression>
= expressions.map { convertExpression(it) }
fun convertExpression(expression: PsiExpression?): Expression {
if (expression == null) return Expression.Empty
return state.expressionConverter.convertExpression(expression, this).assignPrototype(expression)
}
fun convertLocalVariable(variable: PsiLocalVariable): LocalVariable {
val isVal = variable.hasModifierProperty(PsiModifier.FINAL) ||
variable.getInitializer() == null/* we do not know actually and prefer val until we have better analysis*/ ||
!variable.hasWriteAccesses(referenceSearcher, variable.getContainingMethod())
return LocalVariable(variable.declarationIdentifier(),
annotationConverter.convertAnnotations(variable),
convertModifiers(variable),
variableTypeToDeclare(variable, settings.specifyLocalVariableTypeByDefault, isVal),
convertExpression(variable.getInitializer(), variable.getType()),
isVal).assignPrototype(variable)
}
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())
if (element.isQualified()) {
@@ -483,19 +405,19 @@ public class Converter private(val project: Project,
return null
}
fun convertTypeElement(element: PsiTypeElement?): TypeElement
public fun convertTypeElement(element: PsiTypeElement?): TypeElement
= TypeElement(if (element == null) ErrorType().assignNoPrototype() else typeConverter.convertType(element.getType())).assignPrototype(element)
private fun convertToNotNullableTypes(types: Array<out PsiType?>): List<Type>
= types.map { typeConverter.convertType(it, Nullability.NotNull) }
fun convertParameterList(parameterList: PsiParameterList): ParameterList
public fun convertParameterList(parameterList: PsiParameterList): ParameterList
= ParameterList(parameterList.getParameters().map { convertParameter(it) }).assignPrototype(parameterList)
fun convertParameter(parameter: PsiParameter,
public fun convertParameter(parameter: PsiParameter,
nullability: Nullability = Nullability.Default,
varValModifier: Parameter.VarValModifier = Parameter.VarValModifier.None,
modifiers: Modifiers = Modifiers.Empty,
modifiers: Modifiers = Modifiers.Empty(),
defaultValue: Expression? = null): Parameter {
var type = typeConverter.convertVariableType(parameter)
when (nullability) {
@@ -503,79 +425,16 @@ public class Converter private(val project: Project,
Nullability.Nullable -> type = type.toNullableType()
}
return Parameter(parameter.declarationIdentifier(), type, varValModifier,
annotationConverter.convertAnnotations(parameter), modifiers, defaultValue).assignPrototype(parameter)
convertAnnotations(parameter), modifiers, defaultValue).assignPrototype(parameter)
}
fun convertExpression(expression: PsiExpression?, expectedType: PsiType?): Expression {
if (expression == null) return Identifier.Empty
var convertedExpression = convertExpression(expression)
if (expectedType == null || expectedType == PsiType.VOID) return convertedExpression
val actualType = expression.getType()
if (actualType == null) return convertedExpression
if (convertedExpression.isNullable &&
(actualType is PsiPrimitiveType || actualType is PsiClassType && expectedType is PsiPrimitiveType)) {
convertedExpression = BangBangExpression(convertedExpression).assignNoPrototype()
}
if (needConversion(actualType, expectedType) && convertedExpression !is LiteralExpression) {
val conversion = PRIMITIVE_TYPE_CONVERSIONS[expectedType.getCanonicalText()]
if (conversion != null) {
convertedExpression = MethodCallExpression.buildNotNull(convertedExpression, conversion)
}
}
return convertedExpression.assignPrototype(expression)
}
fun convertedExpressionType(expression: PsiExpression, expectedType: PsiType): Type {
var convertedExpression = convertExpression(expression)
val actualType = expression.getType()
if (actualType == null) return ErrorType()
var resultType = typeConverter.convertType(actualType, if (convertedExpression.isNullable) Nullability.Nullable else Nullability.NotNull)
if (actualType is PsiPrimitiveType && resultType.isNullable ||
expectedType is PsiPrimitiveType && actualType is PsiClassType) {
resultType = resultType.toNotNullType()
}
if (needConversion(actualType, expectedType) && convertedExpression !is LiteralExpression) {
val conversion = PRIMITIVE_TYPE_CONVERSIONS[expectedType.getCanonicalText()]
if (conversion != null) {
resultType = typeConverter.convertType(expectedType, Nullability.NotNull)
}
}
return resultType
}
private fun needConversion(actual: PsiType, expected: PsiType): Boolean {
val expectedStr = expected.getCanonicalText()
val actualStr = actual.getCanonicalText()
return expectedStr != actualStr &&
expectedStr != typeConversionMap[actualStr] &&
actualStr != typeConversionMap[expectedStr]
}
private val typeConversionMap: Map<String, String> = mapOf(
JAVA_LANG_BYTE to "byte",
JAVA_LANG_SHORT to "short",
JAVA_LANG_INTEGER to "int",
JAVA_LANG_LONG to "long",
JAVA_LANG_FLOAT to "float",
JAVA_LANG_DOUBLE to "double",
JAVA_LANG_CHARACTER to "char"
)
fun convertIdentifier(identifier: PsiIdentifier?): Identifier {
if (identifier == null) return Identifier.Empty
public fun convertIdentifier(identifier: PsiIdentifier?): Identifier {
if (identifier == null) return Identifier.Empty()
return Identifier(identifier.getText()!!).assignPrototype(identifier)
}
fun convertModifiers(owner: PsiModifierListOwner): Modifiers {
public fun convertModifiers(owner: PsiModifierListOwner): Modifiers {
return Modifiers(MODIFIERS_MAP.filter { owner.hasModifierProperty(it.first) }.map { it.second })
.assignPrototype(owner.getModifierList(), CommentsAndSpacesInheritance(blankLinesBefore = false))
}
@@ -592,7 +451,7 @@ public class Converter private(val project: Project,
val types = throwsList.getReferencedTypes()
val refElements = throwsList.getReferenceElements()
assert(types.size == refElements.size)
if (types.isEmpty()) return Annotations.Empty
if (types.isEmpty()) return Annotations.Empty()
val arguments = types.indices.map {
val convertedType = typeConverter.convertType(types[it], Nullability.NotNull)
null to MethodCallExpression.buildNotNull(null, "javaClass", listOf(), listOf(convertedType)).assignPrototype(refElements[it])
@@ -39,20 +39,30 @@ import org.jetbrains.jet.asJava.KotlinLightField
import org.jetbrains.jet.lang.psi.JetObjectDeclaration
trait ExpressionConverter {
fun convertExpression(expression: PsiExpression, converter: Converter): Expression
fun convertExpression(expression: PsiExpression, codeConverter: CodeConverter): Expression
}
trait SpecialExpressionConverter {
fun convertExpression(expression: PsiExpression, codeConverter: CodeConverter): Expression?
}
fun ExpressionConverter.withSpecialConverter(specialConverter: SpecialExpressionConverter): ExpressionConverter {
return object: ExpressionConverter {
override fun convertExpression(expression: PsiExpression, codeConverter: CodeConverter)
= specialConverter.convertExpression(expression, codeConverter) ?: this@withSpecialConverter.convertExpression(expression, codeConverter)
}
}
class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
private var _converter: Converter? = null
private var _codeConverter: CodeConverter? = null
private var result: Expression = Expression.Empty
private val converter: Converter get() {
return _converter!!
}
private val typeConverter: TypeConverter get() = converter.typeConverter
private val codeConverter: CodeConverter get() = _codeConverter!!
private val typeConverter: TypeConverter get() = codeConverter.typeConverter
private val converter: Converter get() = codeConverter.converter
override fun convertExpression(expression: PsiExpression, converter: Converter): Expression {
this._converter = converter
override fun convertExpression(expression: PsiExpression, codeConverter: CodeConverter): Expression {
this._codeConverter = codeConverter
result = Expression.Empty
expression.accept(this)
@@ -62,8 +72,8 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
override fun visitArrayAccessExpression(expression: PsiArrayAccessExpression) {
val assignment = PsiTreeUtil.getParentOfType(expression, javaClass<PsiAssignmentExpression>())
val lvalue = assignment != null && expression == assignment.getLExpression();
result = ArrayAccessExpression(converter.convertExpression(expression.getArrayExpression()),
converter.convertExpression(expression.getIndexExpression()),
result = ArrayAccessExpression(codeConverter.convertExpression(expression.getArrayExpression()),
codeConverter.convertExpression(expression.getIndexExpression()),
lvalue)
}
@@ -71,7 +81,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
val expressionType = typeConverter.convertType(expression.getType())
assert(expressionType is ArrayType, "Array initializer must have array type")
result = createArrayInitializerExpression(expressionType as ArrayType,
converter.convertExpressions(expression.getInitializers()),
codeConverter.convertExpressions(expression.getInitializers()),
needExplicitType = true/*TODO: it's often redundant*/)
}
@@ -87,8 +97,8 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
else -> ""
}
val lhs = converter.convertExpression(expression.getLExpression())
val rhs = converter.convertExpression(expression.getRExpression()!!, expression.getLExpression().getType())
val lhs = codeConverter.convertExpression(expression.getLExpression())
val rhs = codeConverter.convertExpression(expression.getRExpression()!!, expression.getLExpression().getType())
if (!secondOp.isEmpty()) {
result = AssignmentExpression(lhs, BinaryExpression(lhs, rhs, secondOp).assignNoPrototype(), " = ")
}
@@ -107,8 +117,8 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
else -> null
}
val lhs = converter.convertExpression(expression.getLOperand(), operandsExpectedType)
val rhs = converter.convertExpression(expression.getROperand(), operandsExpectedType)
val lhs = codeConverter.convertExpression(expression.getLOperand(), operandsExpectedType)
val rhs = codeConverter.convertExpression(expression.getROperand(), operandsExpectedType)
if (expression.getOperationTokenType() == JavaTokenType.GTGTGT) {
result = MethodCallExpression.buildNotNull(lhs, "ushr", listOf(rhs))
}
@@ -147,18 +157,18 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
val condition = expression.getCondition()
val type = condition.getType()
val expr = if (type != null)
converter.convertExpression(condition, type)
codeConverter.convertExpression(condition, type)
else
converter.convertExpression(condition)
codeConverter.convertExpression(condition)
result = IfStatement(expr,
converter.convertExpression(expression.getThenExpression()),
converter.convertExpression(expression.getElseExpression()),
codeConverter.convertExpression(expression.getThenExpression()),
codeConverter.convertExpression(expression.getElseExpression()),
expression.isInSingleLine())
}
override fun visitInstanceOfExpression(expression: PsiInstanceOfExpression) {
val checkType = expression.getCheckType()
result = IsOperator(converter.convertExpression(expression.getOperand()),
result = IsOperator(codeConverter.convertExpression(expression.getOperand()),
converter.convertTypeElement(checkType))
}
@@ -196,7 +206,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
val methodExpr = expression.getMethodExpression()
val arguments = expression.getArgumentList().getExpressions()
val target = methodExpr.resolve()
val isNullable = if (target is PsiMethod) typeConverter.methodNullability(target).isNullable(converter.settings) else false
val isNullable = if (target is PsiMethod) typeConverter.methodNullability(target).isNullable(codeConverter.settings) else false
val typeArguments = convertTypeArguments(expression)
if (target is KotlinLightMethod) {
@@ -213,12 +223,12 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
val isExtension = property.isExtensionDeclaration()
val propertyAccess = if (isTopLevel) {
if (isExtension)
QualifiedExpression(converter.convertExpression(arguments.firstOrNull()), propertyName).assignNoPrototype()
QualifiedExpression(codeConverter.convertExpression(arguments.firstOrNull()), propertyName).assignNoPrototype()
else
propertyName
}
else {
QualifiedExpression(converter.convertExpression(methodExpr.getQualifierExpression()), propertyName).assignNoPrototype()
QualifiedExpression(codeConverter.convertExpression(methodExpr.getQualifierExpression()), propertyName).assignNoPrototype()
}
when(if (isExtension) parameterCount - 1 else parameterCount) {
@@ -228,7 +238,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
}
1 /* setter */ -> {
val argument = converter.convertExpression(arguments[if (isExtension) 1 else 0])
val argument = codeConverter.convertExpression(arguments[if (isExtension) 1 else 0])
result = AssignmentExpression(propertyAccess, argument, "=")
return
}
@@ -238,7 +248,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
else if (origin is JetFunction) {
if (isTopLevel) {
result = if (origin.isExtensionDeclaration()) {
val qualifier = converter.convertExpression(arguments.firstOrNull())
val qualifier = codeConverter.convertExpression(arguments.firstOrNull())
MethodCallExpression.build(qualifier,
origin.getName()!!,
convertArguments(expression, isExtension = true),
@@ -260,7 +270,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
if (target is PsiMethod) {
val specialMethod = SpecialMethod.values().firstOrNull { it.matches(target) }
if (specialMethod != null && arguments.size == specialMethod.parameterCount) {
val converted = specialMethod.convertCall(methodExpr.getQualifierExpression(), arguments, typeArguments, converter)
val converted = specialMethod.convertCall(methodExpr.getQualifierExpression(), arguments, typeArguments, codeConverter)
if (converted != null) {
result = converted
return
@@ -268,7 +278,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
}
}
result = MethodCallExpression(converter.convertExpression(methodExpr),
result = MethodCallExpression(codeConverter.convertExpression(methodExpr),
convertArguments(expression),
typeArguments,
isNullable)
@@ -297,12 +307,12 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
override fun visitNewExpression(expression: PsiNewExpression) {
if (expression.getArrayInitializer() != null) {
result = converter.convertExpression(expression.getArrayInitializer())
result = codeConverter.convertExpression(expression.getArrayInitializer())
}
else if (expression.getArrayDimensions().size > 0 && expression.getType() is PsiArrayType) {
result = ArrayWithoutInitializationExpression(
typeConverter.convertType(expression.getType(), Nullability.NotNull) as ArrayType,
converter.convertExpressions(expression.getArrayDimensions()))
codeConverter.convertExpressions(expression.getArrayDimensions()))
}
else {
val anonymousClass = expression.getAnonymousClass()
@@ -311,22 +321,22 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
val classRefConverted = if (classRef != null) converter.convertCodeReferenceElement(classRef, hasExternalQualifier = qualifier != null) else null
result = NewClassExpression(classRefConverted,
convertArguments(expression),
converter.convertExpression(qualifier),
if (anonymousClass != null) converter.convertAnonymousClassBody(anonymousClass) else null)
codeConverter.convertExpression(qualifier),
if (anonymousClass != null) codeConverter.convertAnonymousClassBody(anonymousClass) else null)
}
}
override fun visitParenthesizedExpression(expression: PsiParenthesizedExpression) {
result = ParenthesizedExpression(converter.convertExpression(expression.getExpression()))
result = ParenthesizedExpression(codeConverter.convertExpression(expression.getExpression()))
}
override fun visitPostfixExpression(expression: PsiPostfixExpression) {
result = PostfixOperator(getOperatorString(expression.getOperationSign().getTokenType()),
converter.convertExpression(expression.getOperand()))
codeConverter.convertExpression(expression.getOperand()))
}
override fun visitPrefixExpression(expression: PsiPrefixExpression) {
val operand = converter.convertExpression(expression.getOperand(), expression.getOperand()!!.getType())
val operand = codeConverter.convertExpression(expression.getOperand(), expression.getOperand()!!.getType())
val token = expression.getOperationTokenType()
if (token == JavaTokenType.TILDE) {
result = MethodCallExpression.buildNotNull(operand, "inv")
@@ -342,7 +352,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
override fun visitReferenceExpression(expression: PsiReferenceExpression) {
val referenceName = expression.getReferenceName()!!
val target = expression.getReference()?.resolve()
val isNullable = target is PsiVariable && typeConverter.variableNullability(target).isNullable(converter.settings)
val isNullable = target is PsiVariable && typeConverter.variableNullability(target).isNullable(codeConverter.settings)
val qualifier = expression.getQualifierExpression()
var identifier = Identifier(referenceName, isNullable).assignNoPrototype()
@@ -351,7 +361,7 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
}
else if (qualifier != null) {
if (target is KotlinLightField<*, *> && target.getOrigin() is JetObjectDeclaration) {
result = converter.convertExpression(qualifier)
result = codeConverter.convertExpression(qualifier)
return
}
}
@@ -382,19 +392,19 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
}
}
result = if (qualifier != null) QualifiedExpression(converter.convertExpression(qualifier), identifier) else identifier
result = if (qualifier != null) QualifiedExpression(codeConverter.convertExpression(qualifier), identifier) else identifier
}
override fun visitSuperExpression(expression: PsiSuperExpression) {
val psiQualifier = expression.getQualifier()
val qualifier = psiQualifier?.getReferenceName()
result = SuperExpression(if (qualifier != null) Identifier(qualifier).assignPrototype(psiQualifier) else Identifier.Empty)
result = SuperExpression(if (qualifier != null) Identifier(qualifier).assignPrototype(psiQualifier) else Identifier.Empty())
}
override fun visitThisExpression(expression: PsiThisExpression) {
val psiQualifier = expression.getQualifier()
val qualifier = psiQualifier?.getReferenceName()
result = ThisExpression(if (qualifier != null) Identifier(qualifier).assignPrototype(psiQualifier) else Identifier.Empty)
result = ThisExpression(if (qualifier != null) Identifier(qualifier).assignPrototype(psiQualifier) else Identifier.Empty())
}
override fun visitTypeCastExpression(expression: PsiTypeCastExpression) {
@@ -404,15 +414,15 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
val typeText = castType.getType().getCanonicalText()
val typeConversion = PRIMITIVE_TYPE_CONVERSIONS[typeText]
if (operandType is PsiPrimitiveType && typeConversion != null) {
result = MethodCallExpression.buildNotNull(converter.convertExpression(operand), typeConversion)
result = MethodCallExpression.buildNotNull(codeConverter.convertExpression(operand), typeConversion)
}
else {
result = TypeCastExpression(typeConverter.convertType(castType.getType()), converter.convertExpression(operand))
result = TypeCastExpression(typeConverter.convertType(castType.getType()), codeConverter.convertExpression(operand))
}
}
override fun visitPolyadicExpression(expression: PsiPolyadicExpression) {
val args = expression.getOperands().map { converter.convertExpression(it, expression.getType()) }
val args = expression.getOperands().map { codeConverter.convertExpression(it, expression.getType()) }
result = PolyadicExpression(args, getOperatorString(expression.getOperationTokenType()))
}
@@ -428,14 +438,14 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter {
return if (arguments.size == expectedTypes.size())
(0..arguments.lastIndex).map { i ->
val argument = arguments[i]
val converted = converter.convertExpression(argument, expectedTypes[i])
val converted = codeConverter.convertExpression(argument, expectedTypes[i])
if (parameters != null && i == arguments.lastIndex && parameters[i].isVarArgs() && argument.getType() is PsiArrayType)
StarExpression(converted).assignNoPrototype()
else
converted
}
else
arguments.map { converter.convertExpression(it) }
arguments.map { codeConverter.convertExpression(it) }
}
private fun getOperatorString(tokenType: IElementType): String {
+21 -17
View File
@@ -55,7 +55,11 @@ import org.jetbrains.jet.j2k.ast.MethodCallExpression
import org.jetbrains.jet.j2k.ast.LambdaExpression
import org.jetbrains.jet.j2k.ast.Statement
class ForConverter(private val statement: PsiForStatement, private val converter: Converter) {
class ForConverter(private val statement: PsiForStatement, private val codeConverter: CodeConverter) {
private val referenceSearcher = codeConverter.converter.referenceSearcher
private val settings = codeConverter.settings
private val project = codeConverter.converter.project
private val initialization = statement.getInitialization()
private val update = statement.getUpdate()
private val condition = statement.getCondition()
@@ -70,11 +74,11 @@ class ForConverter(private val statement: PsiForStatement, private val converter
val condition = statement.getCondition()
val body = statement.getBody()
val initializationConverted = converter.convertStatement(initialization)
val updateConverted = converter.convertStatement(update)
val initializationConverted = codeConverter.convertStatement(initialization)
val updateConverted = codeConverter.convertStatement(update)
val whileBody = if (updateConverted.isEmpty) {
converter.convertStatementOrBlock(body)
codeConverter.convertStatementOrBlock(body)
}
else if (body is PsiBlockStatement) {
val nameConflict = initialization is PsiDeclarationStatement && initialization.getDeclaredElements().any { loopVar ->
@@ -86,21 +90,21 @@ class ForConverter(private val statement: PsiForStatement, private val converter
}
if (nameConflict) {
val statements = listOf(converter.convertStatement(body), updateConverted)
val statements = listOf(codeConverter.convertStatement(body), updateConverted)
Block(statements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype(), true).assignNoPrototype()
}
else {
val block = converter.convertBlock(body.getCodeBlock(), true)
val block = codeConverter.convertBlock(body.getCodeBlock(), true)
Block(block.statements + listOf(updateConverted), block.lBrace, block.rBrace, true).assignPrototypesFrom(block)
}
}
else {
val statements = listOf(converter.convertStatement(body), updateConverted)
val statements = listOf(codeConverter.convertStatement(body), updateConverted)
Block(statements, LBrace().assignNoPrototype(), RBrace().assignNoPrototype(), true).assignNoPrototype()
}
val whileStatement = WhileStatement(
if (condition != null) converter.convertExpression(condition) else LiteralExpression("true").assignNoPrototype(),
if (condition != null) codeConverter.convertExpression(condition) else LiteralExpression("true").assignNoPrototype(),
whileBody,
statement.isInSingleLine()).assignNoPrototype()
if (initializationConverted.isEmpty) return whileStatement
@@ -114,8 +118,8 @@ class ForConverter(private val statement: PsiForStatement, private val converter
if (initialization is PsiDeclarationStatement) {
val loopVar = initialization.getDeclaredElements().singleOrNull2() as? PsiLocalVariable
if (loopVar != null
&& !loopVar.hasWriteAccesses(converter.referenceSearcher, body)
&& !loopVar.hasWriteAccesses(converter.referenceSearcher, condition)
&& !loopVar.hasWriteAccesses(referenceSearcher, body)
&& !loopVar.hasWriteAccesses(referenceSearcher, condition)
&& condition is PsiBinaryExpression) {
val operationTokenType = condition.getOperationTokenType()
val lowerBound = condition.getLOperand()
@@ -128,11 +132,11 @@ class ForConverter(private val statement: PsiForStatement, private val converter
if (start != null &&
(update as? PsiExpressionStatement)?.getExpression()?.isVariablePlusPlus(loopVar) ?: false) {
val range = forIterationRange(start, upperBound, operationTokenType).assignNoPrototype()
val explicitType = if (converter.settings.specifyLocalVariableTypeByDefault)
val explicitType = if (settings.specifyLocalVariableTypeByDefault)
PrimitiveType(Identifier("Int").assignNoPrototype()).assignNoPrototype()
else
null
return ForeachStatement(loopVar.declarationIdentifier(), explicitType, range, converter.convertStatementOrBlock(body), statement.isInSingleLine())
return ForeachStatement(loopVar.declarationIdentifier(), explicitType, range, codeConverter.convertStatementOrBlock(body), statement.isInSingleLine())
}
}
}
@@ -160,10 +164,10 @@ class ForConverter(private val statement: PsiForStatement, private val converter
if (methodExpr is PsiReferenceExpression && methodExpr.getReferenceName() == "size") {
val qualifier = methodExpr.getQualifierExpression()
if (qualifier is PsiReferenceExpression /* we don't convert to .indices if qualifier is method call or something because of possible side effects */) {
val listType = PsiElementFactory.SERVICE.getInstance(converter.project).createTypeByFQClassName(CommonClassNames.JAVA_UTIL_LIST)
val listType = PsiElementFactory.SERVICE.getInstance(project).createTypeByFQClassName(CommonClassNames.JAVA_UTIL_LIST)
val qualifierType = qualifier.getType()
if (qualifierType != null && listType.isAssignableFrom(qualifierType)) {
return QualifiedExpression(converter.convertExpression(qualifier), Identifier("indices", false).assignNoPrototype())
return QualifiedExpression(codeConverter.convertExpression(qualifier), Identifier("indices", false).assignNoPrototype())
}
}
}
@@ -174,17 +178,17 @@ class ForConverter(private val statement: PsiForStatement, private val converter
&& upperBound.getReferenceName() == "length") {
val qualifier = upperBound.getQualifierExpression()
if (qualifier is PsiReferenceExpression && qualifier.getType() is PsiArrayType) {
return QualifiedExpression(converter.convertExpression(qualifier), Identifier("indices", false).assignNoPrototype())
return QualifiedExpression(codeConverter.convertExpression(qualifier), Identifier("indices", false).assignNoPrototype())
}
}
}
val end = converter.convertExpression(upperBound)
val end = codeConverter.convertExpression(upperBound)
val endExpression = if (comparisonTokenType == JavaTokenType.LT)
BinaryExpression(end, LiteralExpression("1").assignNoPrototype(), "-").assignNoPrototype()
else
end
return RangeExpression(converter.convertExpression(start), endExpression)
return RangeExpression(codeConverter.convertExpression(start), endExpression)
}
}
@@ -0,0 +1,91 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.j2k
import com.intellij.psi.PsiElement
import com.intellij.openapi.progress.ProcessCanceledException
import org.jetbrains.jet.j2k.ast.Element
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiJavaFile
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.lang.resolve.BindingContext
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.jet.j2k.usageProcessing.UsageProcessingExpressionConverter
public trait ConversionScope {
public fun contains(element: PsiElement): Boolean
}
public class FilesConversionScope(val files: Collection<PsiJavaFile>) : ConversionScope {
override fun contains(element: PsiElement) = files.any { element.getContainingFile() == it }
}
public trait PostProcessor {
public val contextToAnalyzeIn: PsiElement
public fun analyzeFile(file: JetFile): BindingContext
public fun doAdditionalProcessing(file: JetFile)
}
public class JavaToKotlinConverter(private val project: Project,
private val settings: ConverterSettings,
private val conversionScope: ConversionScope,
private val referenceSearcher: ReferenceSearcher,
private val postProcessor: PostProcessor?) {
private val LOG = Logger.getInstance("#org.jetbrains.jet.j2k.JavaToKotlinConverter")
//TODO: run not for one element when multiple files
public fun elementToKotlin(psiElement: PsiElement): String {
try {
val converter = Converter.create(psiElement, settings, conversionScope, referenceSearcher, postProcessor)
val element = converter.convert() ?: return ""
val codeConverter = converter.createDefaultCodeConverter()
.withSpecialExpressionConverter(UsageProcessingExpressionConverter(converter.usageProcessings))
converter.unfoldLazyElements(codeConverter)
val builder = CodeBuilder(psiElement)
builder.append(element)
if (postProcessor != null) {
try {
return AfterConversionPass(project, postProcessor).run(builder.result)
}
catch(e: ProcessCanceledException) {
throw e
}
catch(t: Throwable) {
LOG.error(t)
return builder.result
}
}
else {
return builder.result
}
}
catch(e: ElementCreationStackTraceRequiredException) {
// if we got this exception then we need to turn element creation stack traces on to get better diagnostic
Element.saveCreationStacktraces = true
try {
return elementToKotlin(psiElement)
}
finally {
Element.saveCreationStacktraces = false
}
}
}
}
@@ -90,7 +90,7 @@ public object JavaToKotlinTranslator {
fun generateKotlinCode(javaCode: String): String {
val file = createFile(javaCode)
if (file is PsiJavaFile) {
val converter = Converter.create(file.getProject(), ConverterSettings.defaultSettings, FilesConversionScope(listOf(file)), EmptyReferenceSearcher, null)
val converter = JavaToKotlinConverter(file.getProject(), ConverterSettings.defaultSettings, FilesConversionScope(listOf(file)), EmptyReferenceSearcher, null)
return prettify(converter.elementToKotlin(file))
}
return ""
+14 -14
View File
@@ -33,47 +33,47 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
override fun matches(method: PsiMethod)
= super.matches(method) && method.getParameterList().getParameters().single().getType().getCanonicalText() == JAVA_LANG_OBJECT
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, converter: Converter): Expression? {
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter): Expression? {
if (qualifier == null || qualifier is PsiSuperExpression) return null
return BinaryExpression(converter.convertExpression(qualifier), converter.convertExpression(arguments.single()), "==")
return BinaryExpression(codeConverter.convertExpression(qualifier), codeConverter.convertExpression(arguments.single()), "==")
}
}
OBJECT_GET_CLASS: SpecialMethod("java.lang.Object", "getClass", 0) {
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, converter: Converter): Expression {
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter): Expression {
val identifier = Identifier("javaClass", false).assignNoPrototype()
return if (qualifier != null) QualifiedExpression(converter.convertExpression(qualifier), identifier) else identifier
return if (qualifier != null) QualifiedExpression(codeConverter.convertExpression(qualifier), identifier) else identifier
}
}
OBJECTS_EQUALS: SpecialMethod("java.util.Objects", "equals", 2) {
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, converter: Converter)
= BinaryExpression(converter.convertExpression(arguments[0]), converter.convertExpression(arguments[1]), "==")
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter)
= BinaryExpression(codeConverter.convertExpression(arguments[0]), codeConverter.convertExpression(arguments[1]), "==")
}
COLLECTIONS_EMPTY_LIST: SpecialMethod("java.util.Collections", "emptyList", 0) {
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, converter: Converter)
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter)
= MethodCallExpression.build(null, "listOf", listOf(), typeArgumentsConverted, false)
}
COLLECTIONS_EMPTY_SET: SpecialMethod("java.util.Collections", "emptySet", 0) {
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, converter: Converter)
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter)
= MethodCallExpression.build(null, "setOf", listOf(), typeArgumentsConverted, false)
}
COLLECTIONS_EMPTY_MAP: SpecialMethod("java.util.Collections", "emptyMap", 0) {
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, converter: Converter)
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter)
= MethodCallExpression.build(null, "mapOf", listOf(), typeArgumentsConverted, false)
}
COLLECTIONS_SINGLETON_LIST: SpecialMethod("java.util.Collections", "singletonList", 1) {
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, converter: Converter)
= MethodCallExpression.build(null, "listOf", listOf(converter.convertExpression(arguments.single())), typeArgumentsConverted, false)
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter)
= MethodCallExpression.build(null, "listOf", listOf(codeConverter.convertExpression(arguments.single())), typeArgumentsConverted, false)
}
COLLECTIONS_SINGLETON: SpecialMethod("java.util.Collections", "singleton", 1) {
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, converter: Converter)
= MethodCallExpression.build(null, "setOf", listOf(converter.convertExpression(arguments.single())), typeArgumentsConverted, false)
override fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter)
= MethodCallExpression.build(null, "setOf", listOf(codeConverter.convertExpression(arguments.single())), typeArgumentsConverted, false)
}
open fun matches(method: PsiMethod): Boolean {
@@ -82,5 +82,5 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String
return method.getParameterList().getParametersCount() == parameterCount
}
abstract fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, converter: Converter): Expression?
abstract fun convertCall(qualifier: PsiExpression?, arguments: Array<PsiExpression>, typeArgumentsConverted: List<Type>, codeConverter: CodeConverter): Expression?
}
@@ -21,19 +21,18 @@ import org.jetbrains.jet.j2k.ast.*
import java.util.ArrayList
trait StatementConverter {
fun convertStatement(statement: PsiStatement, converter: Converter): Statement
fun convertStatement(statement: PsiStatement, codeConverter: CodeConverter): Statement
}
class DefaultStatementConverter : JavaElementVisitor(), StatementConverter {
private var _converter: Converter? = null
private var _codeConverter: CodeConverter? = null
private var result: Statement = Statement.Empty
private val converter: Converter get() {
return _converter!!
}
private val codeConverter: CodeConverter get() = _codeConverter!!
private val converter: Converter get() = codeConverter.converter
override fun convertStatement(statement: PsiStatement, converter: Converter): Statement {
this._converter = converter
override fun convertStatement(statement: PsiStatement, codeConverter: CodeConverter): Statement {
this._codeConverter = codeConverter
result = Statement.Empty
statement.accept(this)
@@ -42,12 +41,12 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter {
override fun visitAssertStatement(statement: PsiAssertStatement) {
val descriptionExpr = statement.getAssertDescription()
val condition = converter.convertExpression(statement.getAssertCondition())
val condition = codeConverter.convertExpression(statement.getAssertCondition())
if (descriptionExpr == null) {
result = MethodCallExpression.buildNotNull(null, "assert", listOf(condition))
}
else {
val description = converter.convertExpression(descriptionExpr)
val description = codeConverter.convertExpression(descriptionExpr)
if (descriptionExpr is PsiLiteralExpression) {
result = MethodCallExpression.buildNotNull(null, "assert", listOf(condition, description))
}
@@ -60,13 +59,13 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter {
}
override fun visitBlockStatement(statement: PsiBlockStatement) {
val block = converter.convertBlock(statement.getCodeBlock())
val block = codeConverter.convertBlock(statement.getCodeBlock())
result = MethodCallExpression.build(null, "run", listOf(), listOf(), false, LambdaExpression(null, block).assignNoPrototype())
}
override fun visitBreakStatement(statement: PsiBreakStatement) {
if (statement.getLabelIdentifier() == null) {
result = BreakStatement(Identifier.Empty)
result = BreakStatement(Identifier.Empty())
}
else {
result = BreakStatement(converter.convertIdentifier(statement.getLabelIdentifier()))
@@ -75,7 +74,7 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter {
override fun visitContinueStatement(statement: PsiContinueStatement) {
if (statement.getLabelIdentifier() == null) {
result = ContinueStatement(Identifier.Empty)
result = ContinueStatement(Identifier.Empty())
}
else {
result = ContinueStatement(converter.convertIdentifier(statement.getLabelIdentifier()))
@@ -85,7 +84,7 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter {
override fun visitDeclarationStatement(statement: PsiDeclarationStatement) {
result = DeclarationStatement(statement.getDeclaredElements().map {
when (it) {
is PsiLocalVariable -> converter.convertLocalVariable(it)
is PsiLocalVariable -> codeConverter.convertLocalVariable(it)
is PsiClass -> converter.convertClass(it)
else -> Element.Empty //what else can be here?
}
@@ -95,73 +94,73 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter {
override fun visitDoWhileStatement(statement: PsiDoWhileStatement) {
val condition = statement.getCondition()
val expression = if (condition != null && condition.getType() != null)
converter.convertExpression(condition, condition.getType())
codeConverter.convertExpression(condition, condition.getType())
else
converter.convertExpression(condition)
result = DoWhileStatement(expression, converter.convertStatementOrBlock(statement.getBody()), statement.isInSingleLine())
codeConverter.convertExpression(condition)
result = DoWhileStatement(expression, codeConverter.convertStatementOrBlock(statement.getBody()), statement.isInSingleLine())
}
override fun visitExpressionStatement(statement: PsiExpressionStatement) {
result = converter.convertExpression(statement.getExpression())
result = codeConverter.convertExpression(statement.getExpression())
}
override fun visitExpressionListStatement(statement: PsiExpressionListStatement) {
result = ExpressionListStatement(converter.convertExpressions(statement.getExpressionList().getExpressions()))
result = ExpressionListStatement(codeConverter.convertExpressions(statement.getExpressionList().getExpressions()))
}
override fun visitForStatement(statement: PsiForStatement) {
result = ForConverter(statement, converter).execute()
result = ForConverter(statement, codeConverter).execute()
}
override fun visitForeachStatement(statement: PsiForeachStatement) {
val iteratorExpr = converter.convertExpression(statement.getIteratedValue())
val iteratorExpr = codeConverter.convertExpression(statement.getIteratedValue())
val iterator = if (iteratorExpr.isNullable) BangBangExpression(iteratorExpr).assignNoPrototype() else iteratorExpr
val iterationParameter = statement.getIterationParameter()
result = ForeachStatement(iterationParameter.declarationIdentifier(),
if (converter.settings.specifyLocalVariableTypeByDefault) converter.typeConverter.convertVariableType(iterationParameter) else null,
if (codeConverter.settings.specifyLocalVariableTypeByDefault) codeConverter.typeConverter.convertVariableType(iterationParameter) else null,
iterator,
converter.convertStatementOrBlock(statement.getBody()),
codeConverter.convertStatementOrBlock(statement.getBody()),
statement.isInSingleLine())
}
override fun visitIfStatement(statement: PsiIfStatement) {
val condition = statement.getCondition()
val expression = converter.convertExpression(condition, PsiType.BOOLEAN)
val expression = codeConverter.convertExpression(condition, PsiType.BOOLEAN)
result = IfStatement(expression,
converter.convertStatementOrBlock(statement.getThenBranch()),
converter.convertStatementOrBlock(statement.getElseBranch()),
codeConverter.convertStatementOrBlock(statement.getThenBranch()),
codeConverter.convertStatementOrBlock(statement.getElseBranch()),
statement.isInSingleLine())
}
override fun visitLabeledStatement(statement: PsiLabeledStatement) {
result = LabelStatement(converter.convertIdentifier(statement.getLabelIdentifier()),
converter.convertStatement(statement.getStatement()))
codeConverter.convertStatement(statement.getStatement()))
}
override fun visitSwitchLabelStatement(statement: PsiSwitchLabelStatement) {
result = if (statement.isDefaultCase())
ElseWhenEntrySelector()
else
ValueWhenEntrySelector(converter.convertExpression(statement.getCaseValue()))
ValueWhenEntrySelector(codeConverter.convertExpression(statement.getCaseValue()))
}
override fun visitSwitchStatement(statement: PsiSwitchStatement) {
result = SwitchConverter(converter).convert(statement)
result = SwitchConverter(codeConverter).convert(statement)
}
override fun visitSynchronizedStatement(statement: PsiSynchronizedStatement) {
result = SynchronizedStatement(converter.convertExpression(statement.getLockExpression()),
converter.convertBlock(statement.getBody()))
result = SynchronizedStatement(codeConverter.convertExpression(statement.getLockExpression()),
codeConverter.convertBlock(statement.getBody()))
}
override fun visitThrowStatement(statement: PsiThrowStatement) {
result = ThrowStatement(converter.convertExpression(statement.getException()))
result = ThrowStatement(codeConverter.convertExpression(statement.getException()))
}
override fun visitTryStatement(tryStatement: PsiTryStatement) {
val tryBlock = tryStatement.getTryBlock()
val catchesConverted = convertCatches(tryStatement)
val finallyConverted = converter.convertBlock(tryStatement.getFinallyBlock())
val finallyConverted = codeConverter.convertBlock(tryStatement.getFinallyBlock())
val resourceList = tryStatement.getResourceList()
if (resourceList != null) {
@@ -172,13 +171,13 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter {
}
}
result = TryStatement(converter.convertBlock(tryBlock), catchesConverted, finallyConverted)
result = TryStatement(codeConverter.convertBlock(tryBlock), catchesConverted, finallyConverted)
}
private fun convertCatches(tryStatement: PsiTryStatement): List<CatchStatement> {
val catches = ArrayList<CatchStatement>()
for ((block, parameter) in tryStatement.getCatchBlocks().zip(tryStatement.getCatchBlockParameters())) {
val blockConverted = converter.convertBlock(block)
val blockConverted = codeConverter.convertBlock(block)
val annotations = converter.convertAnnotations(parameter)
val parameterType = parameter.getType()
val types = if (parameterType is PsiDisjunctionType)
@@ -186,12 +185,12 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter {
else
listOf(parameterType)
for (t in types) {
var convertedType = converter.typeConverter.convertType(t, Nullability.NotNull)
var convertedType = codeConverter.typeConverter.convertType(t, Nullability.NotNull)
val convertedParameter = Parameter(parameter.declarationIdentifier(),
convertedType,
Parameter.VarValModifier.None,
annotations,
Modifiers.Empty).assignPrototype(parameter)
Modifiers.Empty()).assignPrototype(parameter)
catches.add(CatchStatement(convertedParameter, blockConverted).assignNoPrototype())
}
}
@@ -200,13 +199,13 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter {
private fun convertTryWithResources(tryBlock: PsiCodeBlock?, resourceVariables: List<PsiResourceVariable>, catchesConverted: List<CatchStatement>, finallyConverted: Block): Statement {
var wrapResultStatement: (Expression) -> Statement = { it }
var converterForBody = converter
var converterForBody = codeConverter
var block = converterForBody.convertBlock(tryBlock)
var expression: Expression = Expression.Empty
for (variable in resourceVariables.reverse()) {
val lambda = LambdaExpression(Identifier.toKotlin(variable.getName()!!), block)
expression = MethodCallExpression.build(converter.convertExpression(variable.getInitializer()), "use", listOf(), listOf(), false, lambda)
expression = MethodCallExpression.build(codeConverter.convertExpression(variable.getInitializer()), "use", listOf(), listOf(), false, lambda)
expression.assignNoPrototype()
block = Block(listOf(expression), LBrace().assignNoPrototype(), RBrace().assignNoPrototype()).assignNoPrototype()
}
@@ -236,19 +235,19 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter {
override fun visitWhileStatement(statement: PsiWhileStatement) {
val condition = statement.getCondition()
val expression = if (condition?.getType() != null)
converter.convertExpression(condition, condition!!.getType())
codeConverter.convertExpression(condition, condition!!.getType())
else
converter.convertExpression(condition)
result = WhileStatement(expression, converter.convertStatementOrBlock(statement.getBody()), statement.isInSingleLine())
codeConverter.convertExpression(condition)
result = WhileStatement(expression, codeConverter.convertStatementOrBlock(statement.getBody()), statement.isInSingleLine())
}
override fun visitReturnStatement(statement: PsiReturnStatement) {
val returnValue = statement.getReturnValue()
val methodReturnType = converter.methodReturnType
val methodReturnType = codeConverter.methodReturnType
val expression = if (returnValue != null && methodReturnType != null)
converter.convertExpression(returnValue, methodReturnType)
codeConverter.convertExpression(returnValue, methodReturnType)
else
converter.convertExpression(returnValue)
codeConverter.convertExpression(returnValue)
result = ReturnStatement(expression)
}
@@ -257,7 +256,7 @@ class DefaultStatementConverter : JavaElementVisitor(), StatementConverter {
}
}
fun Converter.convertStatementOrBlock(statement: PsiStatement?): Statement {
fun CodeConverter.convertStatementOrBlock(statement: PsiStatement?): Statement {
return if (statement is PsiBlockStatement)
convertBlock(statement.getCodeBlock())
else
@@ -20,9 +20,9 @@ import com.intellij.psi.*
import org.jetbrains.jet.j2k.ast.*
import java.util.ArrayList
class SwitchConverter(private val converter: Converter) {
class SwitchConverter(private val codeConverter: CodeConverter) {
public fun convert(statement: PsiSwitchStatement): WhenStatement
= WhenStatement(converter.convertExpression(statement.getExpression()), switchBodyToWhenEntries(statement.getBody()))
= WhenStatement(codeConverter.convertExpression(statement.getExpression()), switchBodyToWhenEntries(statement.getBody()))
private class Case(val label: PsiSwitchLabelStatement?, val statements: List<PsiStatement>)
@@ -38,7 +38,7 @@ class SwitchConverter(private val converter: Converter) {
result.add(WhenEntry(listOf(ValueWhenEntrySelector(Expression.Empty).assignNoPrototype()), convertCaseStatementsToBody(cases, i)).assignNoPrototype())
continue
}
pendingSelectors.add(converter.convertStatement(case.label) as WhenEntrySelector)
pendingSelectors.add(codeConverter.convertStatement(case.label) as WhenEntrySelector)
if (case.statements.isNotEmpty()) {
result.add(WhenEntry(pendingSelectors, convertCaseStatementsToBody(cases, i)).assignNoPrototype())
pendingSelectors = ArrayList()
@@ -81,10 +81,10 @@ class SwitchConverter(private val converter: Converter) {
if (allowBlock && statementsToKeep.size == 1) {
val block = statementsToKeep.single() as? PsiBlockStatement
if (block != null) {
return listOf(converter.convertBlock(block.getCodeBlock(), true, { !isSwitchBreak(it) }))
return listOf(codeConverter.convertBlock(block.getCodeBlock(), true, { !isSwitchBreak(it) }))
}
}
return statementsToKeep.map { converter.convertStatement(it) }
return statementsToKeep.map { codeConverter.convertStatement(it) }
}
private fun convertCaseStatements(cases: List<Case>, caseIndex: Int, allowBlock: Boolean = true): List<Statement> {
+1 -1
View File
@@ -63,7 +63,7 @@ class TypeVisitor(private val converter: Converter) : PsiTypeVisitor<Type>() {
if (kotlinClassName != null) {
val kotlinShortName = getShortName(kotlinClassName)
if (kotlinShortName == getShortName(javaClassName!!) && converter.importNames.contains(getPackageName(javaClassName) + ".*")) {
converter.importsToAdd?.add(kotlinClassName)
converter.importsToAdd.add(kotlinClassName)
}
return ReferenceElement(Identifier(kotlinShortName).assignNoPrototype(), typeArgs).assignNoPrototype()
}
@@ -59,12 +59,13 @@ class Annotations(val annotations: List<Annotation>) : Element() {
builder.append(annotations, "")
}
override val isEmpty: Boolean = annotations.isEmpty()
override val isEmpty: Boolean
get() = annotations.isEmpty()
fun plus(other: Annotations) = Annotations(annotations + other.annotations)
class object {
val Empty = Annotations(listOf())
fun Empty() = Annotations(listOf())
}
}
@@ -19,7 +19,7 @@ package org.jetbrains.jet.j2k.ast
import org.jetbrains.jet.j2k.CodeBuilder
class AnonymousClassBody(body: ClassBody, val extendsTrait: Boolean)
: Class(Identifier.Empty, Annotations.Empty, Modifiers.Empty, TypeParameterList.Empty, listOf(), listOf(), listOf(), body) {
: Class(Identifier.Empty(), Annotations.Empty(), Modifiers.Empty(), TypeParameterList.Empty(), listOf(), listOf(), listOf(), body) {
override fun generateCode(builder: CodeBuilder) {
body.append(builder)
}
+1 -1
View File
@@ -32,7 +32,7 @@ class Block(val statements: List<Statement>, val lBrace: LBrace, val rBrace: RBr
}
class object {
val Empty = Block(listOf(), LBrace(), RBrace())
fun Empty() = Block(listOf(), LBrace(), RBrace())
}
}
@@ -30,13 +30,14 @@ class ClassBody (
val rBrace: RBrace) {
fun append(builder: CodeBuilder) {
if (members.isEmpty() && classObjectMembers.isEmpty()) return
val membersFiltered = members.filter { !it.isEmpty }
if (membersFiltered.isEmpty() && classObjectMembers.isEmpty()) return
builder append " " append lBrace append "\n"
builder.append(members, "\n")
builder.append(membersFiltered, "\n")
appendClassObject(builder, members.isNotEmpty())
appendClassObject(builder, membersFiltered.isNotEmpty())
builder append "\n" append rBrace
}
@@ -22,22 +22,24 @@ import com.intellij.util.IncorrectOperationException
class PrimaryConstructor(annotations: Annotations,
modifiers: Modifiers,
val parameterList: ParameterList,
val block: Block)
val body: LazyElement<Block>)
: Member(annotations, modifiers) {
override fun generateCode(builder: CodeBuilder) { throw IncorrectOperationException() }
public fun initializer(): Initializer? {
return if (!block.isEmpty)
Initializer(block, Modifiers.Empty).assignPrototypesFrom(this, CommentsAndSpacesInheritance(commentsBefore = false))
else
null
}
public fun initializer(): Initializer
= Initializer(body, Modifiers.Empty()).assignPrototypesFrom(this, CommentsAndSpacesInheritance(commentsBefore = false))
public fun signature(): PrimaryConstructorSignature {
val noBody = block.isEmpty
val inheritance = CommentsAndSpacesInheritance(blankLinesBefore = false, commentsAfter = noBody, commentsInside = noBody)
return PrimaryConstructorSignature(annotations, modifiers, parameterList).assignPrototypesFrom(this, inheritance)
public fun createSignature(converter: Converter): PrimaryConstructorSignature {
val signature = PrimaryConstructorSignature(annotations, modifiers, parameterList)
// assign prototypes later because we don't know yet whether the body is empty or not
converter.addPostUnfoldLazyElementsAction {
val inheritance = CommentsAndSpacesInheritance(blankLinesBefore = false, commentsAfter = body.isEmpty, commentsInside = body.isEmpty)
signature.assignPrototypesFrom(this, inheritance)
}
return signature
}
}
@@ -64,8 +66,5 @@ class FactoryFunction(name: Identifier,
returnType: Type,
parameterList: ParameterList,
typeParameterList: TypeParameterList,
body: Block)
body: LazyElement<Block>)
: Function(name, annotations, modifiers, returnType, typeParameterList, parameterList, body, false)
fun FactoryFunction.withBody(body: Block)
= FactoryFunction(name, annotations, modifiers, returnType, parameterList, typeParameterList, body).assignPrototypesFrom(this)
+44 -9
View File
@@ -57,15 +57,7 @@ fun Element.canonicalCode(): String {
}
abstract class Element {
public var prototypes: List<PrototypeInfo>? = null
set(value) {
// no prototypes assigned to empty elements because they can be singleton instances (and they are not needed anyway)
if (isEmpty) {
$prototypes = listOf()
return
}
$prototypes = value
}
public open var prototypes: List<PrototypeInfo>? = null
public var createdAt: String?
= if (saveCreationStacktraces)
@@ -83,9 +75,52 @@ abstract class Element {
object Empty : Element() {
override fun generateCode(builder: CodeBuilder) { }
override val isEmpty: Boolean get() = true
override var prototypes: List<PrototypeInfo>? by EmptyElementPrototypes // to not hold references to psi
}
class object {
var saveCreationStacktraces = false
}
}
object EmptyElementPrototypes {
public fun get(thisRef: Element, desc: PropertyMetadata): List<PrototypeInfo>? = null
public fun set(thisRef: Element, desc: PropertyMetadata, value: List<PrototypeInfo>?) { }
}
// this class should never be created directly - Converter.lazyElement() should be used!
class LazyElement<TResult : Element>(private var generator: (CodeConverter) -> TResult) : Element() {
private var result: TResult? = null
{
assignNoPrototype()
}
public fun unfold(codeConverter: CodeConverter) {
assert(result == null)
result = generator(codeConverter)
}
override fun generateCode(builder: CodeBuilder) {
resultNotNull.generateCode(builder)
}
override val isEmpty: Boolean
get() = resultNotNull.isEmpty
private val resultNotNull: TResult
get() {
assert(result != null) { "No code generated for lazy element $this. Possible reason is that it has been created directly instead of Converter.lazyElement() call." }
return result!!
}
public fun updateGenerator(generatorUpdater: (codeConverter: CodeConverter, prevResult: TResult) -> TResult) {
assert(result == null, "Cannot update generator when code has been generated")
val prevGenerator = generator
generator = { codeConverter ->
val prevResult = prevGenerator(codeConverter)
generatorUpdater(codeConverter, prevResult)
}
}
}
@@ -23,7 +23,7 @@ class EnumConstant(
annotations: Annotations,
modifiers: Modifiers,
val type: Type,
val params: Element
val params: LazyElement<ExpressionList>
) : Member(annotations, modifiers) {
override fun generateCode(builder: CodeBuilder) {
@@ -34,6 +34,4 @@ class EnumConstant(
builder append annotations append identifier append " : " append type append "(" append params append ")"
}
}
@@ -17,14 +17,14 @@
package org.jetbrains.jet.j2k.ast
import org.jetbrains.jet.j2k.CodeBuilder
import org.jetbrains.jet.j2k.CodeConverter
abstract class Expression() : Statement() {
open val isNullable: Boolean get() = false
object Empty : Expression() {
override fun generateCode(builder: CodeBuilder) {}
override val isEmpty: Boolean get() = true
override var prototypes: List<PrototypeInfo>? by EmptyElementPrototypes // to not hold references to psi
}
}
+4 -3
View File
@@ -18,12 +18,13 @@ package org.jetbrains.jet.j2k.ast
import org.jetbrains.jet.j2k.*
//TODO: rename it to Property
class Field(
val identifier: Identifier,
annotations: Annotations,
modifiers: Modifiers,
val type: Type,
val initializer: Element,
private val initializer: LazyElement<Expression>,
val isVal: Boolean,
val explicitType: Boolean,
private val defaultInitializer: Boolean,
@@ -40,9 +41,9 @@ class Field(
builder append ":" append type
}
var initializerToUse = initializer
var initializerToUse: Element = initializer
if (initializerToUse.isEmpty && defaultInitializer) {
initializerToUse = getDefaultInitializer(this) ?: Expression.Empty
initializerToUse = getDefaultInitializer(this) ?: Element.Empty
}
if (!initializerToUse.isEmpty) {
builder append " = " append initializerToUse
@@ -25,7 +25,7 @@ open class Function(
val returnType: Type,
val typeParameterList: TypeParameterList,
val parameterList: ParameterList,
val body: Block?,
val body: LazyElement<Block>?,
val isInTrait: Boolean
) : Member(annotations, modifiers) {
@@ -18,10 +18,11 @@ package org.jetbrains.jet.j2k.ast
import org.jetbrains.jet.j2k.CodeBuilder
import com.intellij.psi.PsiNameIdentifierOwner
import org.jetbrains.jet.j2k.CodeConverter
fun PsiNameIdentifierOwner.declarationIdentifier(): Identifier {
val name = getName()
return if (name != null) Identifier(name, false).assignPrototype(getNameIdentifier()!!) else Identifier.Empty
return if (name != null) Identifier(name, false).assignPrototype(getNameIdentifier()!!) else Identifier.Empty()
}
class Identifier(
@@ -50,7 +51,7 @@ class Identifier(
override fun toString() = if (isNullable) "$name?" else name
class object {
val Empty = Identifier("")
fun Empty() = Identifier("")
val ONLY_KOTLIN_KEYWORDS: Set<String> = setOf(
"package", "as", "type", "val", "var", "fun", "is", "in", "object", "when", "trait", "This"
+1 -1
View File
@@ -30,7 +30,7 @@ class Import(val name: String) : Element() {
}
}
class ImportList(public val imports: List<Import>) : Element() {
class ImportList(public var imports: List<Import>) : Element() {
override val isEmpty: Boolean
get() = imports.isEmpty()
@@ -18,8 +18,11 @@ package org.jetbrains.jet.j2k.ast
import org.jetbrains.jet.j2k.*
class Initializer(val block: Block, modifiers: Modifiers) : Member(Annotations.Empty, modifiers) {
class Initializer(val body: LazyElement<Block>, modifiers: Modifiers) : Member(Annotations.Empty(), modifiers) {
override fun generateCode(builder: CodeBuilder) {
builder.append(block)
builder.append(body)
}
override val isEmpty: Boolean
get() = body.isEmpty
}
@@ -60,7 +60,7 @@ class Modifiers(val modifiers: Collection<Modifier>) : Element() {
fun accessModifier(): Modifier? = modifiers.firstOrNull { it in ACCESS_MODIFIERS }
class object {
val Empty = Modifiers(listOf())
fun Empty() = Modifiers(listOf())
}
}
@@ -22,9 +22,8 @@ import org.jetbrains.jet.j2k.*
abstract class Statement() : Element() {
object Empty : Statement() {
override fun generateCode(builder: CodeBuilder) { }
override val isEmpty: Boolean
get() = true
override val isEmpty: Boolean get() = true
override var prototypes: List<PrototypeInfo>? by EmptyElementPrototypes // to not hold references to psi
}
}
@@ -107,13 +106,13 @@ class ForeachStatement(
}
}
class BreakStatement(val label: Identifier = Identifier.Empty) : Statement() {
class BreakStatement(val label: Identifier = Identifier.Empty()) : Statement() {
override fun generateCode(builder: CodeBuilder) {
builder.append("break").appendWithPrefix(label, "@")
}
}
class ContinueStatement(val label: Identifier = Identifier.Empty) : Statement() {
class ContinueStatement(val label: Identifier = Identifier.Empty()) : Statement() {
override fun generateCode(builder: CodeBuilder) {
builder.append("continue").appendWithPrefix(label, "@")
}
@@ -50,14 +50,13 @@ class TypeParameterList(val parameters: List<TypeParameter>) : Element() {
return builder
}
override val isEmpty: Boolean
get() = parameters.isEmpty()
private fun hasWhere(): Boolean = parameters.any { it.hasWhere() }
class object {
val Empty = TypeParameterList(ArrayList())
fun Empty() = TypeParameterList(listOf())
}
}
@@ -70,5 +69,5 @@ fun Converter.convertTypeParameterList(typeParameterList: PsiTypeParameterList?)
return if (typeParameterList != null)
TypeParameterList(typeParameterList.getTypeParameters()!!.toList().map { convertTypeParameter(it) }).assignPrototype(typeParameterList)
else
TypeParameterList.Empty
TypeParameterList.Empty()
}
@@ -0,0 +1,56 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.j2k.usageProcessing
import com.intellij.psi.*
import org.jetbrains.jet.j2k.*
import org.jetbrains.jet.j2k.ast.*
class AccessorToPropertyProcessing(val accessorMethod: PsiMethod, val accessorKind: AccessorKind, val propertyName: String) : UsageProcessing {
override val targetElement: PsiElement get() = accessorMethod
override val convertedCodeProcessor = object: ConvertedCodeProcessor {
override fun convertMethodUsage(methodCall: PsiMethodCallExpression, codeConverter: CodeConverter): Expression? {
val isNullable = codeConverter.typeConverter.methodNullability(accessorMethod).isNullable(codeConverter.settings)
val methodExpr = methodCall.getMethodExpression()
val arguments = methodCall.getArgumentList().getExpressions()
val propertyName = Identifier(propertyName, isNullable).assignNoPrototype()
val propertyAccess = QualifiedExpression(codeConverter.convertExpression(methodExpr.getQualifierExpression()), propertyName).assignNoPrototype()
if (accessorKind == AccessorKind.GETTER) {
if (arguments.size != 0) return null // incorrect call
return propertyAccess
}
else {
if (arguments.size != 1) return null // incorrect call
val argument = codeConverter.convertExpression(arguments[0])
return AssignmentExpression(propertyAccess, argument, "=")
}
}
}
override val javaCodeProcessor: ExternalCodeProcessor? = null
override val kotlinCodeProcessor: ExternalCodeProcessor? = object: ExternalCodeProcessor {
override fun processUsage(reference: PsiReference) {
//TODO
//reference.getElement() as JetSimpleNameExpression
}
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.j2k.usageProcessing
import com.intellij.psi.*
import org.jetbrains.jet.j2k.CodeConverter
import org.jetbrains.jet.j2k.ast.*
class FieldToPropertyProcessing(val field: PsiField, val propertyName: String, val isNullable: Boolean) : UsageProcessing {
override val targetElement: PsiElement get() = field
override val convertedCodeProcessor = if (field.getName() != propertyName) {
object: ConvertedCodeProcessor {
override fun convertVariableUsage(expression: PsiReferenceExpression, codeConverter: CodeConverter): Expression? {
val identifier = Identifier(propertyName, isNullable).assignNoPrototype()
val qualifier = expression.getQualifierExpression()
return if (qualifier != null) {
QualifiedExpression(codeConverter.convertExpression(qualifier), identifier)
}
else {
// check if field name is shadowed
val elementFactory = PsiElementFactory.SERVICE.getInstance(expression.getProject())
val refExpr = elementFactory.createExpressionFromText(propertyName, expression) as PsiReferenceExpression
if (refExpr.resolve() == null)
identifier
else
QualifiedExpression(ThisExpression(Identifier.Empty()).assignNoPrototype(), identifier) //TODO: this is not correct in case of nested/anonymous classes
}
}
}
}
else {
null
}
override val javaCodeProcessor: ExternalCodeProcessor? get() = null //TODO
override val kotlinCodeProcessor: ExternalCodeProcessor? get() = null //TODO
}
@@ -0,0 +1,57 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.j2k.usageProcessing
import org.jetbrains.jet.j2k.ast.Expression
import com.intellij.psi.*
import org.jetbrains.jet.j2k.CodeConverter
import org.jetbrains.jet.j2k.SpecialExpressionConverter
trait UsageProcessing {
val targetElement: PsiElement
val convertedCodeProcessor: ConvertedCodeProcessor?
val javaCodeProcessor: ExternalCodeProcessor?
val kotlinCodeProcessor: ExternalCodeProcessor?
}
trait ConvertedCodeProcessor {
fun convertVariableUsage(expression: PsiReferenceExpression, codeConverter: CodeConverter): Expression? = null
fun convertMethodUsage(methodCall: PsiMethodCallExpression, codeConverter: CodeConverter): Expression? = null
}
trait ExternalCodeProcessor {
fun processUsage(reference: PsiReference)
}
class UsageProcessingExpressionConverter(processings: Collection<UsageProcessing>) : SpecialExpressionConverter {
private val targetToProcessing = processings.toMap { it.targetElement } // we assume that there will be no more than one processing for one target element
override fun convertExpression(expression: PsiExpression, codeConverter: CodeConverter): Expression? {
if (targetToProcessing.isEmpty()) return null
//TODO: method usages
if (expression is PsiReferenceExpression) {
val target = expression.resolve() ?: return null
val processor = targetToProcessing[target]?.convertedCodeProcessor ?: return null
return processor.convertVariableUsage(expression, codeConverter)
}
return null
}
}
@@ -18,8 +18,6 @@ package org.jetbrains.jet.j2k.test
import java.io.File
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.jet.j2k.Converter
import org.jetbrains.jet.j2k.JavaToKotlinTranslator
import org.jetbrains.jet.j2k.ConverterSettings
import java.util.regex.Pattern
import com.intellij.testFramework.LightPlatformTestCase
@@ -40,6 +38,8 @@ import org.jetbrains.jet.j2k.translateToKotlin
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
import org.jetbrains.jet.JetTestCaseBuilder
import com.intellij.openapi.util.Disposer
import org.jetbrains.jet.j2k.JavaToKotlinConverter
import org.jetbrains.jet.j2k.JavaToKotlinTranslator
public abstract class AbstractJavaToKotlinConverterTest() : LightCodeInsightFixtureTestCase() {
val testHeaderPattern = Pattern.compile("//(element|expression|statement|method|class|file|comp)\n")
@@ -136,14 +136,14 @@ public abstract class AbstractJavaToKotlinConverterTest() : LightCodeInsightFixt
private fun elementToKotlin(text: String, settings: ConverterSettings, project: Project): String {
val fileWithText = createJavaFile(text)
val converter = Converter.create(project, settings, FilesConversionScope(listOf(fileWithText)), IdeaReferenceSearcher, J2kPostProcessor(fileWithText))
val converter = JavaToKotlinConverter(project, settings, FilesConversionScope(listOf(fileWithText)), IdeaReferenceSearcher, J2kPostProcessor(fileWithText))
val element = fileWithText.getFirstChild()!!
return converter.elementToKotlin(element)
}
private fun fileToKotlin(text: String, settings: ConverterSettings, project: Project): String {
val file = createJavaFile(text)
val converter = Converter.create(project, settings, FilesConversionScope(listOf(file)), IdeaReferenceSearcher, J2kPostProcessor(file))
val converter = JavaToKotlinConverter(project, settings, FilesConversionScope(listOf(file)), IdeaReferenceSearcher, J2kPostProcessor(file))
return converter.elementToKotlin(file)
}