diff --git a/.idea/dictionaries/valentin.xml b/.idea/dictionaries/valentin.xml index 54354129c96..0bfc803d560 100644 --- a/.idea/dictionaries/valentin.xml +++ b/.idea/dictionaries/valentin.xml @@ -4,6 +4,7 @@ initializers inserter pparent + processings rparenth diff --git a/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinAction.java b/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinAction.java index 201d4eacac7..04468a1a14a 100644 --- a/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinAction.java +++ b/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinAction.java @@ -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$, diff --git a/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinActionUtil.java b/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinActionUtil.java index 4a848965605..ed8fd3931de 100644 --- a/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinActionUtil.java +++ b/idea/src/org/jetbrains/jet/plugin/actions/JavaToKotlinActionUtil.java @@ -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 convertFiles(final Converter converter, List allJavaFilesNear) { + static List convertFiles(final JavaToKotlinConverter converter, List allJavaFilesNear) { final List result = new LinkedList(); 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) { diff --git a/idea/src/org/jetbrains/jet/plugin/conversion/copy/ConvertJavaCopyPastePostProcessor.kt b/idea/src/org/jetbrains/jet/plugin/conversion/copy/ConvertJavaCopyPastePostProcessor.kt index 31b23c15c5a..6a5fdc7ef05 100644 --- a/idea/src/org/jetbrains/jet/plugin/conversion/copy/ConvertJavaCopyPastePostProcessor.kt +++ b/idea/src/org/jetbrains/jet/plugin/conversion/copy/ConvertJavaCopyPastePostProcessor.kt @@ -86,7 +86,7 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor { 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() diff --git a/j2k/src/org/jetbrains/jet/j2k/ClassBodyConverter.kt b/j2k/src/org/jetbrains/jet/j2k/ClassBodyConverter.kt index 2c190b9342c..a63b30acb3d 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ClassBodyConverter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ClassBodyConverter.kt @@ -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 - } - - } - } - } - } } \ No newline at end of file diff --git a/j2k/src/org/jetbrains/jet/j2k/CodeConverter.kt b/j2k/src/org/jetbrains/jet/j2k/CodeConverter.kt new file mode 100644 index 00000000000..8ad53ef746f --- /dev/null +++ b/j2k/src/org/jetbrains/jet/j2k/CodeConverter.kt @@ -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): List + = 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 = 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" + ) + + +} \ No newline at end of file diff --git a/j2k/src/org/jetbrains/jet/j2k/ConstructorConverter.kt b/j2k/src/org/jetbrains/jet/j2k/ConstructorConverter.kt index ef02b091e97..3ff53639152 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ConstructorConverter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ConstructorConverter.kt @@ -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() 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() 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? { @@ -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()).filter { it.isVal } - val updatedFactoryFunctions = ArrayList() 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): List { - 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) { + 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, - 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) : 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 } } } \ No newline at end of file diff --git a/j2k/src/org/jetbrains/jet/j2k/Converter.kt b/j2k/src/org/jetbrains/jet/j2k/Converter.kt index ff4c745a01c..48082c270de 100644 --- a/j2k/src/org/jetbrains/jet/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/Converter.kt @@ -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) : ConversionScope { - override fun contains(element: PsiElement) = files.any { element.getContainingFile() == it } -} + private class State(val specialContext: PsiElement?, + val importsToAdd: MutableSet, + val lazyElements: MutableList>, + val usageProcessings: MutableCollection, + 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?) + public val specialContext: PsiElement? = state.specialContext + public val importsToAdd: MutableCollection = state.importsToAdd - val typeConverter: TypeConverter = TypeConverter(this) - - val methodReturnType: PsiType? = state.methodReturnType - val specialContext: PsiElement? = state.specialContext - val importNames: Set = state.importList?.imports?.mapTo(HashSet()) { it.name } ?: setOf() - val importsToAdd: MutableCollection? = state.importsToAdd - - val annotationConverter = AnnotationConverter(this) + private val importList = (elementToConvert as? PsiJavaFile)?.getImportList()?.let { convertImportList(it) } + public val importNames: Set = importList?.imports?.mapTo(HashSet()) { 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): 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 + 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 lazyElement(generator: (CodeConverter) -> TResult): LazyElement { + 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() - 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()).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?, constructorConverter: ConstructorConverter?): Member? { - return withMethodReturnType(method.getReturnType()).doConvertMethod(method, membersToRemove, constructorConverter)?.assignPrototype(method) - } - - private fun doConvertMethod(method: PsiMethod, membersToRemove: MutableSet?, constructorConverter: ConstructorConverter?): Member? { + public fun convertMethod(method: PsiMethod, membersToRemove: MutableSet?, 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() 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): List - = 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? = null): ReferenceElement { + public fun convertCodeReferenceElement(element: PsiJavaCodeReferenceElement, hasExternalQualifier: Boolean, typeArgsConverted: List? = 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): List = 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 = 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]) diff --git a/j2k/src/org/jetbrains/jet/j2k/ExpressionConverter.kt b/j2k/src/org/jetbrains/jet/j2k/ExpressionConverter.kt index 6d416b83b6d..07b2e13f59d 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ExpressionConverter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ExpressionConverter.kt @@ -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()) 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 { diff --git a/j2k/src/org/jetbrains/jet/j2k/ForConverter.kt b/j2k/src/org/jetbrains/jet/j2k/ForConverter.kt index a9354a2f0ec..e10be4adb41 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ForConverter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ForConverter.kt @@ -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) } } \ No newline at end of file diff --git a/j2k/src/org/jetbrains/jet/j2k/JavaToKotlinConverter.kt b/j2k/src/org/jetbrains/jet/j2k/JavaToKotlinConverter.kt new file mode 100644 index 00000000000..e4bceb4c36d --- /dev/null +++ b/j2k/src/org/jetbrains/jet/j2k/JavaToKotlinConverter.kt @@ -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) : 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 + } + } + } +} \ No newline at end of file diff --git a/j2k/src/org/jetbrains/jet/j2k/JavaToKotlinTranslator.kt b/j2k/src/org/jetbrains/jet/j2k/JavaToKotlinTranslator.kt index 522674fbf0c..006f77ed77d 100644 --- a/j2k/src/org/jetbrains/jet/j2k/JavaToKotlinTranslator.kt +++ b/j2k/src/org/jetbrains/jet/j2k/JavaToKotlinTranslator.kt @@ -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 "" diff --git a/j2k/src/org/jetbrains/jet/j2k/SpecialMethod.kt b/j2k/src/org/jetbrains/jet/j2k/SpecialMethod.kt index a23780ed2da..d4c48fe1be5 100644 --- a/j2k/src/org/jetbrains/jet/j2k/SpecialMethod.kt +++ b/j2k/src/org/jetbrains/jet/j2k/SpecialMethod.kt @@ -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, typeArgumentsConverted: List, converter: Converter): Expression? { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, 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, typeArgumentsConverted: List, converter: Converter): Expression { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, 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, typeArgumentsConverted: List, converter: Converter) - = BinaryExpression(converter.convertExpression(arguments[0]), converter.convertExpression(arguments[1]), "==") + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, 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, typeArgumentsConverted: List, converter: Converter) + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, 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, typeArgumentsConverted: List, converter: Converter) + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, 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, typeArgumentsConverted: List, converter: Converter) + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, 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, typeArgumentsConverted: List, converter: Converter) - = MethodCallExpression.build(null, "listOf", listOf(converter.convertExpression(arguments.single())), typeArgumentsConverted, false) + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, 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, typeArgumentsConverted: List, converter: Converter) - = MethodCallExpression.build(null, "setOf", listOf(converter.convertExpression(arguments.single())), typeArgumentsConverted, false) + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, 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, typeArgumentsConverted: List, converter: Converter): Expression? + abstract fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter): Expression? } \ No newline at end of file diff --git a/j2k/src/org/jetbrains/jet/j2k/StatementConverter.kt b/j2k/src/org/jetbrains/jet/j2k/StatementConverter.kt index fb48f4d4976..51cb8001628 100644 --- a/j2k/src/org/jetbrains/jet/j2k/StatementConverter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/StatementConverter.kt @@ -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 { val catches = ArrayList() 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, catchesConverted: List, 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 diff --git a/j2k/src/org/jetbrains/jet/j2k/SwitchConverter.kt b/j2k/src/org/jetbrains/jet/j2k/SwitchConverter.kt index 8fe34cc9f39..932f3f56cf0 100644 --- a/j2k/src/org/jetbrains/jet/j2k/SwitchConverter.kt +++ b/j2k/src/org/jetbrains/jet/j2k/SwitchConverter.kt @@ -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) @@ -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, caseIndex: Int, allowBlock: Boolean = true): List { diff --git a/j2k/src/org/jetbrains/jet/j2k/TypeVisitor.kt b/j2k/src/org/jetbrains/jet/j2k/TypeVisitor.kt index 8a74d730c5e..d3c2b4a0181 100644 --- a/j2k/src/org/jetbrains/jet/j2k/TypeVisitor.kt +++ b/j2k/src/org/jetbrains/jet/j2k/TypeVisitor.kt @@ -63,7 +63,7 @@ class TypeVisitor(private val converter: Converter) : PsiTypeVisitor() { 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() } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Annotation.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Annotation.kt index 775b608715a..b96ae0560bf 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Annotation.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Annotation.kt @@ -59,12 +59,13 @@ class Annotations(val annotations: List) : 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()) } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt b/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt index 2691210e640..1c4fd847be3 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/AnonymousClassBody.kt @@ -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) } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Block.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Block.kt index 15b417ffe06..35a77d5e286 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Block.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Block.kt @@ -32,7 +32,7 @@ class Block(val statements: List, val lBrace: LBrace, val rBrace: RBr } class object { - val Empty = Block(listOf(), LBrace(), RBrace()) + fun Empty() = Block(listOf(), LBrace(), RBrace()) } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/ClassBody.kt b/j2k/src/org/jetbrains/jet/j2k/ast/ClassBody.kt index d2ce78f18fa..6bd425c3e81 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/ClassBody.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/ClassBody.kt @@ -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 } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt index 1f837a6a977..f941db0aed4 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Constructors.kt @@ -22,22 +22,24 @@ import com.intellij.util.IncorrectOperationException class PrimaryConstructor(annotations: Annotations, modifiers: Modifiers, val parameterList: ParameterList, - val block: Block) + val body: LazyElement) : 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) : Function(name, annotations, modifiers, returnType, typeParameterList, parameterList, body, false) - -fun FactoryFunction.withBody(body: Block) - = FactoryFunction(name, annotations, modifiers, returnType, parameterList, typeParameterList, body).assignPrototypesFrom(this) \ No newline at end of file diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Element.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Element.kt index 38a1ebb313b..dbe8e155ca3 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Element.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Element.kt @@ -57,15 +57,7 @@ fun Element.canonicalCode(): String { } abstract class Element { - public var prototypes: List? = 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? = 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? by EmptyElementPrototypes // to not hold references to psi } class object { var saveCreationStacktraces = false } } + +object EmptyElementPrototypes { + public fun get(thisRef: Element, desc: PropertyMetadata): List? = null + public fun set(thisRef: Element, desc: PropertyMetadata, value: List?) { } +} + +// this class should never be created directly - Converter.lazyElement() should be used! +class LazyElement(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) + } + } +} + diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/EnumConstant.kt b/j2k/src/org/jetbrains/jet/j2k/ast/EnumConstant.kt index 3d7f17c2988..2dc770ab980 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/EnumConstant.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/EnumConstant.kt @@ -23,7 +23,7 @@ class EnumConstant( annotations: Annotations, modifiers: Modifiers, val type: Type, - val params: Element + val params: LazyElement ) : 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 ")" } - - } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Expression.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Expression.kt index e9fa4713ae3..04705e34a3c 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Expression.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Expression.kt @@ -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? by EmptyElementPrototypes // to not hold references to psi } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt index 7ea324efc6e..5e5623ff0cc 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Field.kt @@ -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, 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 diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt index 775f0792bee..95f3d21a14e 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Function.kt @@ -25,7 +25,7 @@ open class Function( val returnType: Type, val typeParameterList: TypeParameterList, val parameterList: ParameterList, - val body: Block?, + val body: LazyElement?, val isInTrait: Boolean ) : Member(annotations, modifiers) { diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Identifier.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Identifier.kt index d2bf0331d0c..42d6dc9ba3e 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Identifier.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Identifier.kt @@ -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 = setOf( "package", "as", "type", "val", "var", "fun", "is", "in", "object", "when", "trait", "This" diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt index 4bce8bea9a8..cea8c79d2f0 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Imports.kt @@ -30,7 +30,7 @@ class Import(val name: String) : Element() { } } -class ImportList(public val imports: List) : Element() { +class ImportList(public var imports: List) : Element() { override val isEmpty: Boolean get() = imports.isEmpty() diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Initializer.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Initializer.kt index 4be86f4d9bf..a57326d76bd 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Initializer.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Initializer.kt @@ -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, modifiers: Modifiers) : Member(Annotations.Empty(), modifiers) { override fun generateCode(builder: CodeBuilder) { - builder.append(block) + builder.append(body) } + + override val isEmpty: Boolean + get() = body.isEmpty } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Modifier.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Modifier.kt index 27f6d277ddf..2e0230b3183 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Modifier.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Modifier.kt @@ -60,7 +60,7 @@ class Modifiers(val modifiers: Collection) : Element() { fun accessModifier(): Modifier? = modifiers.firstOrNull { it in ACCESS_MODIFIERS } class object { - val Empty = Modifiers(listOf()) + fun Empty() = Modifiers(listOf()) } } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/Statements.kt b/j2k/src/org/jetbrains/jet/j2k/ast/Statements.kt index 96c8319182c..435aedc2bf8 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/Statements.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/Statements.kt @@ -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? 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, "@") } diff --git a/j2k/src/org/jetbrains/jet/j2k/ast/TypeParameters.kt b/j2k/src/org/jetbrains/jet/j2k/ast/TypeParameters.kt index 3247ad06855..8d539404bb5 100644 --- a/j2k/src/org/jetbrains/jet/j2k/ast/TypeParameters.kt +++ b/j2k/src/org/jetbrains/jet/j2k/ast/TypeParameters.kt @@ -50,14 +50,13 @@ class TypeParameterList(val parameters: List) : 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() } \ No newline at end of file diff --git a/j2k/src/org/jetbrains/jet/j2k/usageProcessing/AccessorToPropertyProcessing.kt b/j2k/src/org/jetbrains/jet/j2k/usageProcessing/AccessorToPropertyProcessing.kt new file mode 100644 index 00000000000..48e1e0715a2 --- /dev/null +++ b/j2k/src/org/jetbrains/jet/j2k/usageProcessing/AccessorToPropertyProcessing.kt @@ -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 + } + } +} \ No newline at end of file diff --git a/j2k/src/org/jetbrains/jet/j2k/usageProcessing/FieldToPropertyProcessing.kt b/j2k/src/org/jetbrains/jet/j2k/usageProcessing/FieldToPropertyProcessing.kt new file mode 100644 index 00000000000..ad879b41803 --- /dev/null +++ b/j2k/src/org/jetbrains/jet/j2k/usageProcessing/FieldToPropertyProcessing.kt @@ -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 +} \ No newline at end of file diff --git a/j2k/src/org/jetbrains/jet/j2k/usageProcessing/UsageProcessing.kt b/j2k/src/org/jetbrains/jet/j2k/usageProcessing/UsageProcessing.kt new file mode 100644 index 00000000000..917c3723e99 --- /dev/null +++ b/j2k/src/org/jetbrains/jet/j2k/usageProcessing/UsageProcessing.kt @@ -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) : 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 + } +} \ No newline at end of file diff --git a/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt b/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt index f802419267e..b0f08373111 100644 --- a/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt +++ b/j2k/tests/test/org/jetbrains/jet/j2k/test/AbstractJavaToKotlinConverterTest.kt @@ -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) }