From ce9a98652468a2dfbff5556247eb16aea7738d7b Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Wed, 27 May 2015 19:18:15 +0300 Subject: [PATCH] J2K: updated to use of Kotlin's KClass instead of Java's class in annotation declarations and usages --- .../jetbrains/kotlin/psi/JetCodeFragment.kt | 6 - .../kotlin/idea/actions/JavaToKotlinAction.kt | 10 +- .../copy/ConvertJavaCopyPastePostProcessor.kt | 7 +- .../kotlin/idea/j2k/J2kPostProcessor.kt | 7 + .../changeSignature/JetChangeInfo.kt | 2 +- ...nIntroduceParameterMethodUsageProcessor.kt | 2 +- .../idea/refactoring/jetRefactoringUtil.kt | 8 +- .../idea/util/ImportInsertHelperImpl.kt | 63 +++--- .../kotlin/j2k/AfterConversionPass.kt | 14 +- .../kotlin/j2k/AnnotationConverter.kt | 13 +- .../org/jetbrains/kotlin/j2k/CodeBuilder.kt | 12 +- j2k/src/org/jetbrains/kotlin/j2k/Converter.kt | 28 ++- .../kotlin/j2k/JavaToKotlinConverter.kt | 192 +++++++++--------- .../kotlin/j2k/JavaToKotlinTranslator.kt | 4 +- .../org/jetbrains/kotlin/j2k/TypeConverter.kt | 9 +- .../org/jetbrains/kotlin/j2k/TypeVisitor.kt | 19 +- .../org/jetbrains/kotlin/j2k/ast/Element.kt | 2 +- .../jetbrains/kotlin/j2k/ast/Identifier.kt | 8 +- .../annotations/annotationUsages.kt | 4 +- .../annotations/javaClassArgument.java | 8 + .../annotations/javaClassArgument.kt | 6 + .../annotations/javaClassArrayArgument.java | 11 + .../annotations/javaClassArrayArgument.kt | 9 + ...tractJavaToKotlinConverterMultiFileTest.kt | 8 +- ...ractJavaToKotlinConverterSingleFileTest.kt | 5 +- ...otlinConverterForWebDemoTestGenerated.java | 12 ++ ...otlinConverterSingleFileTestGenerated.java | 12 ++ 27 files changed, 294 insertions(+), 187 deletions(-) create mode 100644 j2k/testData/fileOrElement/annotations/javaClassArgument.java create mode 100644 j2k/testData/fileOrElement/annotations/javaClassArgument.kt create mode 100644 j2k/testData/fileOrElement/annotations/javaClassArrayArgument.java create mode 100644 j2k/testData/fileOrElement/annotations/javaClassArrayArgument.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetCodeFragment.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetCodeFragment.kt index f4e5206ba4f..53b2096afc7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetCodeFragment.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetCodeFragment.kt @@ -102,12 +102,6 @@ public abstract class JetCodeFragment( override fun addImportsFromString(imports: String?) { if (imports == null || imports.isEmpty()) return - - // We should increment modification tracker after inserting import in code fragment to invalidate resolve caches. - // Without this modification references with new import won't be resolved without any modification in code fragment. - // Also shorten references won't work. - (PsiModificationTracker.SERVICE.getInstance(getProject()) as PsiModificationTrackerImpl).incOutOfCodeBlockModificationCounter() - myImports.addAll(imports.split(IMPORT_SEPARATOR)) } diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt index 6c5c0b73580..5f39de3725f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt @@ -47,12 +47,10 @@ public class JavaToKotlinAction : AnAction() { val javaFiles = selectedJavaFiles(e).toList() val project = CommonDataKeys.PROJECT.getData(e.getDataContext())!! - var converterResult: JavaToKotlinConverter.Result? = null + var converterResult: JavaToKotlinConverter.FilesResult? = null fun convert() { - val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, - IdeaReferenceSearcher, IdeaResolverForConverter, J2kPostProcessor(formatCode = true)) - val inputElements = javaFiles.map { JavaToKotlinConverter.InputElement(it, it) } - converterResult = converter.elementsToKotlin(inputElements, ProgressManager.getInstance().getProgressIndicator()) + val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaReferenceSearcher, IdeaResolverForConverter) + converterResult = converter.filesToKotlin(javaFiles, J2kPostProcessor(formatCode = true), ProgressManager.getInstance().getProgressIndicator()) } val title = "Convert Java to Kotlin" @@ -85,7 +83,7 @@ public class JavaToKotlinAction : AnAction() { project.executeWriteCommand("Convert files from Java to Kotlin") { CommandProcessor.getInstance().markCurrentCommandAsGlobal(project) - val newFiles = saveResults(javaFiles, converterResult!!.results.map { it!!.text /*conversion of a file always succeeds*/ }) + val newFiles = saveResults(javaFiles, converterResult!!.results) externalCodeUpdate?.invoke() diff --git a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt index 324cd3012cf..7f93428594d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPastePostProcessor.kt @@ -145,11 +145,10 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor().map { JavaToKotlinConverter.InputElement(it, null) } + val inputElements = elementsAndTexts.filterIsInstance() val results = converter.elementsToKotlin(inputElements).results var resultIndex = 0 @@ -163,6 +162,8 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor Unit)? { val psiElement = problem.getPsiElement() return when (problem.getFactory()) { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeInfo.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeInfo.kt index 8c30a4425b3..6bc7a67b170 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeInfo.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeInfo.kt @@ -301,7 +301,7 @@ public fun ChangeInfo.toJetChangeInfo(originalChangeSignatureDescriptor: JetMeth val defaultValueExpr = if (getLanguage().`is`(JavaLanguage.INSTANCE) && !defaultValueText.isNullOrEmpty()) { PsiElementFactory.SERVICE.getInstance(method.getProject()) .createExpressionFromText(defaultValueText!!, null) - .j2k(originalChangeSignatureDescriptor.baseDeclaration) + .j2k() } else null diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceParameter/KotlinIntroduceParameterMethodUsageProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceParameter/KotlinIntroduceParameterMethodUsageProcessor.kt index af7d537c521..da72c01898e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceParameter/KotlinIntroduceParameterMethodUsageProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceParameter/KotlinIntroduceParameterMethodUsageProcessor.kt @@ -76,7 +76,7 @@ public class KotlinIntroduceParameterMethodUsageProcessor : IntroduceParameterMe data.getParametersToRemove().toNativeArray().toList().sortDescending().forEach { changeInfo.removeParameter(it) } // Temporarily assume that the new parameter is of Any type. Actual type is substituted during the signature update phase - val defaultValueForCall = (data.getParameterInitializer().getExpression()!! as? PsiExpression)?.let { it.j2k(it) } + val defaultValueForCall = (data.getParameterInitializer().getExpression()!! as? PsiExpression)?.let { it.j2k() } changeInfo.addParameter(JetParameterInfo(functionDescriptor = psiMethodDescriptor, name = data.getParameterName(), type = KotlinBuiltIns.getInstance().getAnyType(), diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt index 631829f2236..3ea12915df6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt @@ -558,16 +558,14 @@ fun createJavaClass(klass: JetClass, targetClass: PsiClass): PsiMember { return javaClass } -fun PsiExpression.j2k(postProcessingContext: PsiElement): JetExpression? { +fun PsiExpression.j2k(): JetExpression? { if (getLanguage() != JavaLanguage.INSTANCE) return null val project = getProject() val j2kConverter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaReferenceSearcher, - IdeaResolverForConverter, - J2kPostProcessor(true)) - val inputElements = Collections.singletonList(JavaToKotlinConverter.InputElement(this, postProcessingContext)) - val text = j2kConverter.elementsToKotlin(inputElements).results.singleOrNull()?.text ?: return null + IdeaResolverForConverter) + val text = j2kConverter.elementsToKotlin(listOf(this)).results.single()?.text ?: return null //TODO: insert imports return JetPsiFactory(getProject()).createExpression(text) } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt b/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt index 561937eec06..e8ebeed0fca 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt +++ b/idea/src/org/jetbrains/kotlin/idea/util/ImportInsertHelperImpl.kt @@ -18,41 +18,37 @@ package org.jetbrains.kotlin.util import com.intellij.codeInsight.CodeInsightSettings import com.intellij.codeInsight.actions.OptimizeImportsProcessor -import org.jetbrains.kotlin.idea.project.ProjectStructureUtil -import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS -import org.jetbrains.kotlin.name.* -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.resolve.ImportPath -import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor -import org.jetbrains.kotlin.descriptors.PackageViewDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.impl.PsiModificationTrackerImpl +import com.intellij.psi.util.PsiModificationTracker +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.core.formatter.JetCodeStyleSettings -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered -import org.jetbrains.kotlin.descriptors.ClassifierDescriptor -import org.jetbrains.kotlin.utils.addIfNotNull -import org.jetbrains.kotlin.resolve.scopes.JetScope -import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.descriptors.Visibilities -import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue -import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility -import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor -import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport +import org.jetbrains.kotlin.idea.imports.getImportableTargets +import org.jetbrains.kotlin.idea.imports.importableFqName +import org.jetbrains.kotlin.idea.imports.importableFqNameSafe +import org.jetbrains.kotlin.idea.project.ProjectStructureUtil +import org.jetbrains.kotlin.idea.refactoring.fqName.isImported import org.jetbrains.kotlin.idea.util.ImportInsertHelper import org.jetbrains.kotlin.idea.util.ImportInsertHelper.ImportDescriptorResult -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.idea.refactoring.fqName.isImported -import java.util.* -import org.jetbrains.kotlin.idea.imports.* +import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.ImportPath +import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor +import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.JetScope +import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.utils.addIfNotNull +import java.util.ArrayList +import java.util.Comparator +import java.util.LinkedHashSet public class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper() { @@ -70,6 +66,11 @@ public class ImportInsertHelperImpl(private val project: Project) : ImportInsert } private fun addImport(file: JetFile, importPath: ImportPath): JetImportDirective { + //TODO: it's a temporary hack for JetCodeFragment's and non-physical files + // We should increment modification tracker after inserting import to invalidate resolve caches. + // Without this modification references with new import won't be resolved. + (PsiModificationTracker.SERVICE.getInstance(project) as PsiModificationTrackerImpl).incOutOfCodeBlockModificationCounter() + val psiFactory = JetPsiFactory(project) if (file is JetCodeFragment) { val newDirective = psiFactory.createImportDirective(importPath) diff --git a/j2k/src/org/jetbrains/kotlin/j2k/AfterConversionPass.kt b/j2k/src/org/jetbrains/kotlin/j2k/AfterConversionPass.kt index 10cbc50b672..4c4cade5729 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/AfterConversionPass.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/AfterConversionPass.kt @@ -39,10 +39,16 @@ public class AfterConversionPass(val project: Project, val postProcessor: PostPr } .filterNotNull() - val document = kotlinFile.getViewProvider().getDocument()!! - val rangeMarker = if (range != null) document.createRangeMarker(range.getStartOffset(), range.getEndOffset()) else null - rangeMarker?.setGreedyToLeft(true) - rangeMarker?.setGreedyToRight(true) + val rangeMarker = if (range != null) { + val document = kotlinFile.getViewProvider().getDocument()!! + val marker = document.createRangeMarker(range.getStartOffset(), range.getEndOffset()) + marker.setGreedyToLeft(true) + marker.setGreedyToRight(true) + marker + } + else { + null + } for ((psiElement, fix) in fixes) { if (psiElement.isValid()) { diff --git a/j2k/src/org/jetbrains/kotlin/j2k/AnnotationConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/AnnotationConverter.kt index 16f3cc04e05..c23ed641926 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/AnnotationConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/AnnotationConverter.kt @@ -121,7 +121,18 @@ class AnnotationConverter(private val converter: Converter) { private fun convertAttributeValue(value: PsiAnnotationMemberValue?, expectedType: PsiType?, isVararg: Boolean, isUnnamed: Boolean): List<(CodeConverter) -> Expression> { when (value) { - is PsiExpression -> return listOf({ codeConverter -> codeConverter.convertExpression(value, expectedType).assignPrototype(value) }) + is PsiExpression -> { + return listOf({ codeConverter -> + val expression = if (value is PsiClassObjectAccessExpression) { + val typeElement = converter.convertTypeElement(value.getOperand()) + ClassLiteralExpression(typeElement.type.toNotNullType()) + } + else { + codeConverter.convertExpression(value, expectedType) + } + expression.assignPrototype(value) + }) + } is PsiArrayInitializerMemberValue -> { val componentType = (expectedType as? PsiArrayType)?.getComponentType() diff --git a/j2k/src/org/jetbrains/kotlin/j2k/CodeBuilder.kt b/j2k/src/org/jetbrains/kotlin/j2k/CodeBuilder.kt index 30ea9ab21ce..66d59397733 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/CodeBuilder.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/CodeBuilder.kt @@ -21,6 +21,7 @@ import com.intellij.psi.* import com.intellij.psi.javadoc.PsiDocComment import org.jetbrains.kotlin.j2k.ast.CommentsAndSpacesInheritance import org.jetbrains.kotlin.j2k.ast.Element +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.psiUtil.isAncestor import java.util.ArrayList import java.util.HashSet @@ -55,9 +56,15 @@ class CodeBuilder(private val topElement: PsiElement?) { private val commentsAndSpacesUsed = HashSet() + private val imports = ArrayList() + public fun append(text: String): CodeBuilder = append(text, false) + public fun addImport(fqName: FqName) { + imports.add(fqName) + } + private fun appendCommentOrWhiteSpace(element: PsiElement) { if (element is PsiDocComment) { append(DocCommentConverter.convertDocComment(element), false) @@ -83,9 +90,12 @@ class CodeBuilder(private val topElement: PsiElement?) { return this } - public val result: String + public val resultText: String get() = builder.toString() + public val importsToAdd: Collection + get() = imports + public fun append(element: Element): CodeBuilder { if (element.isEmpty) return this // do not insert comment and spaces for empty elements to avoid multiple blank lines diff --git a/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt b/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt index a879227b37a..a08e45a8c47 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/Converter.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.j2k.ast.Object import org.jetbrains.kotlin.j2k.usageProcessing.FieldToPropertyProcessing import org.jetbrains.kotlin.j2k.usageProcessing.UsageProcessing import org.jetbrains.kotlin.j2k.usageProcessing.UsageProcessingExpressionConverter +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.types.expressions.OperatorConventions.* import java.util.ArrayList import java.util.HashMap @@ -77,10 +78,15 @@ class Converter private( private fun createDefaultCodeConverter() = CodeConverter(this, DefaultExpressionConverter(), DefaultStatementConverter(), null) public data class IntermediateResult( - val codeGenerator: (Map>) -> String, + val codeGenerator: (Map>) -> Result, val parseContext: ParseContext ) + public data class Result( + val text: String, + val importsToAdd: Collection + ) + public fun convert(): IntermediateResult? { val element = convertTopElement(elementToConvert) ?: return null val parseContext = when (elementToConvert) { @@ -93,7 +99,7 @@ class Converter private( val builder = CodeBuilder(elementToConvert) builder.append(element) - builder.result + Result(builder.resultText, builder.importsToAdd) }, parseContext) } @@ -253,24 +259,24 @@ class Converter private( annotationConverter.convertAnnotationMethodDefault(method)).assignPrototype(method, noBlankLinesInheritance) } + fun convertType(psiType: PsiType?): Type { + return typeConverter.convertType(psiType, Nullability.NotNull, inAnnotationType = true) + } + val parameters = // Argument named `value` comes first if it exists // Convert it as vararg if it's array - methodsNamedValue. - map { method -> + methodsNamedValue.map { method -> val returnType = method.getReturnType() val typeConverted = if (returnType is PsiArrayType) - VarArgType(typeConverter.convertType(returnType.getComponentType(), Nullability.NotNull)) + VarArgType(convertType(returnType.getComponentType())) else - typeConverter.convertType(returnType, Nullability.NotNull) + convertType(returnType) createParameter(typeConverted, method) } + - otherMethods - .map { method -> - val typeConverted = typeConverter.convertType(method.getReturnType(), Nullability.NotNull) - createParameter(typeConverted, method) - } + otherMethods.map { method -> createParameter(convertType(method.getReturnType()), method) } + val parameterList = ParameterList(parameters).assignNoPrototype() val constructorSignature = if (parameterList.parameters.isNotEmpty()) PrimaryConstructorSignature(Annotations.Empty, Modifiers.Empty, parameterList).assignNoPrototype() diff --git a/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt index aa292aa2c81..e7fa64d98b2 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinConverter.kt @@ -26,16 +26,14 @@ import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.Project import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile -import com.intellij.psi.PsiNamedElement -import com.intellij.psi.PsiReference +import com.intellij.psi.* import com.intellij.psi.impl.source.DummyHolder import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.JetLanguage import org.jetbrains.kotlin.j2k.ast.Element import org.jetbrains.kotlin.j2k.usageProcessing.UsageProcessing +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isAncestor import org.jetbrains.kotlin.psi.psiUtil.parents @@ -45,9 +43,11 @@ import java.util.Comparator import java.util.LinkedHashMap import kotlin.properties.Delegates -public trait PostProcessor { +public interface PostProcessor { public fun analyzeFile(file: JetFile, range: TextRange?): BindingContext + public fun insertImport(file: JetFile, fqName: FqName) + public open fun fixForProblem(problem: Diagnostic): (() -> Unit)? { val psiElement = problem.getPsiElement() return when (problem.getFactory()) { @@ -77,30 +77,57 @@ public enum class ParseContext { CODE_BLOCK } -public class JavaToKotlinConverter(private val project: Project, - private val settings: ConverterSettings, - private val referenceSearcher: ReferenceSearcher, - private val resolverForConverter: ResolverForConverter, - private val postProcessor: PostProcessor?) { +public class JavaToKotlinConverter( + private val project: Project, + private val settings: ConverterSettings, + private val referenceSearcher: ReferenceSearcher, + private val resolverForConverter: ResolverForConverter +) { private val LOG = Logger.getInstance("#org.jetbrains.kotlin.j2k.JavaToKotlinConverter") - public data class InputElement( - val element: PsiElement, - val postProcessingContext: PsiElement? - ) - - public data class ElementResult(val text: String, val parseContext: ParseContext) - - public trait ExternalCodeProcessing { + public interface ExternalCodeProcessing { public fun prepareWriteOperation(progress: ProgressIndicator): () -> Unit } + public data class ElementResult(val text: String, val importsToAdd: Collection, val parseContext: ParseContext) + public data class Result(val results: List, val externalCodeProcessing: ExternalCodeProcessing?) - public fun elementsToKotlin( - inputElements: List, - progress: ProgressIndicator = EmptyProgressIndicator() - ): Result { + public data class FilesResult(val results: List, val externalCodeProcessing: ExternalCodeProcessing?) + + public fun filesToKotlin(files: List, postProcessor: PostProcessor, progress: ProgressIndicator = EmptyProgressIndicator()): FilesResult { + val withProgressProcessor = WithProgressProcessor(progress, files) + + val (results, externalCodeProcessing) = elementsToKotlin(files, withProgressProcessor) + + val texts = withProgressProcessor.processItems(0.5, results.withIndex()) { pair -> + val (i, result) = pair + try { + val kotlinFile = JetPsiFactory(project).createAnalyzableFile("dummy.kt", result!!.text, files[i]) + + result.importsToAdd.forEach { postProcessor.insertImport(kotlinFile, it) } + + AfterConversionPass(project, postProcessor).run(kotlinFile, range = null) + + kotlinFile.getText() + } + catch(e: ProcessCanceledException) { + throw e + } + catch(t: Throwable) { + LOG.error(t) + result!!.text + } + } + + return FilesResult(texts, externalCodeProcessing) + } + + public fun elementsToKotlin(inputElements: List): Result { + return elementsToKotlin(inputElements, WithProgressProcessor.DEFAULT) + } + + private fun elementsToKotlin(inputElements: List, processor: WithProgressProcessor): Result { try { val usageProcessings = LinkedHashMap>() val usageProcessingCollector: (UsageProcessing) -> Unit = { @@ -108,87 +135,25 @@ public class JavaToKotlinConverter(private val project: Project, } fun inConversionScope(element: PsiElement) - = inputElements.any { it.element.isAncestor(element, strict = false) } + = inputElements.any { it.isAncestor(element, strict = false) } - val progressText = "Converting Java to Kotlin" - val elementCount = inputElements.size() - val fileCountText = elementCount.toString() + " " + if (elementCount > 1) "files" else "file" - var fraction = 0.0 - var pass = 1 - fun processFilesWithProgress( - fractionPortion: Double, - inputItems: Iterable, - processItem: (TInputItem) -> TOutputItem - ): List { - val outputItems = ArrayList(elementCount) - // we use special process with EmptyProgressIndicator to avoid changing text in our progress by inheritors search inside etc - ProgressManager.getInstance().runProcess( - { - progress.setText("$progressText ($fileCountText) - pass $pass of 3") - - for ((i, item) in inputItems.withIndex()) { - progress.checkCanceled() - progress.setFraction(fraction + fractionPortion * i / elementCount) - - val psiFile = inputElements[i].element as? PsiFile - if (psiFile != null) { - progress.setText2(psiFile.getVirtualFile().getPresentableUrl()) - } - - outputItems.add(processItem(item)) - } - - pass++ - fraction += fractionPortion - }, - EmptyProgressIndicator()) - return outputItems - } - - val intermediateResults = processFilesWithProgress(0.25, inputElements) { inputElement -> - Converter.create(inputElement.element, settings, ::inConversionScope, referenceSearcher, resolverForConverter, usageProcessingCollector).convert() + val intermediateResults = processor.processItems(0.25, inputElements) { inputElement -> + Converter.create(inputElement, settings, ::inConversionScope, referenceSearcher, resolverForConverter, usageProcessingCollector).convert() }.toArrayList() - val results = processFilesWithProgress(0.25, intermediateResults.withIndex()) { pair -> + val results = processor.processItems(0.25, intermediateResults.withIndex()) { pair -> val (i, result) = pair intermediateResults[i] = null // to not hold unused objects in the heap - if (result != null) - ElementResult(result.codeGenerator(usageProcessings), result.parseContext) - else - null + result?.let { + val (text, importsToAdd) = it.codeGenerator(usageProcessings) + ElementResult(text, importsToAdd, it.parseContext) + } } val externalCodeProcessing = buildExternalCodeProcessing(usageProcessings, ::inConversionScope) - if (postProcessor == null) { - assert(progress is EmptyProgressIndicator, "Progress indicator not supported for postProcessor == null") - return Result(results, externalCodeProcessing) - } - - val finalResults = processFilesWithProgress(0.5, results.withIndex()) { pair -> - val (i, result) = pair - if (result != null) { - try { - //TODO: post processing does not work correctly for ParseContext different from TOP_LEVEL - val kotlinFile = JetPsiFactory(project).createAnalyzableFile("dummy.kt", result.text, inputElements[i].postProcessingContext!!) - AfterConversionPass(project, postProcessor).run(kotlinFile, range = null) - ElementResult(kotlinFile.getText(), result.parseContext) - } - catch(e: ProcessCanceledException) { - throw e - } - catch(t: Throwable) { - LOG.error(t) - result - } - } - else { - null - } - } - - return Result(finalResults, externalCodeProcessing) + return Result(results, externalCodeProcessing) } catch(e: ElementCreationStackTraceRequiredException) { // if we got this exception then we need to turn element creation stack traces on to get better diagnostic @@ -202,7 +167,7 @@ public class JavaToKotlinConverter(private val project: Project, } } - data class ReferenceInfo( + private data class ReferenceInfo( val reference: PsiReference, val target: PsiElement, val file: PsiFile, @@ -295,6 +260,45 @@ public class JavaToKotlinConverter(private val project: Project, } } + private class WithProgressProcessor(private val progress: ProgressIndicator?, private val files: List?) { + public companion object { + val DEFAULT = WithProgressProcessor(null, null) + } + + private val progressText = "Converting Java to Kotlin" + private val fileCount = files?.size() ?: 0 + private val fileCountText = fileCount.toString() + " " + if (fileCount > 1) "files" else "file" + private var fraction = 0.0 + private var pass = 1 + + fun processItems( + fractionPortion: Double, + inputItems: Iterable, + processItem: (TInputItem) -> TOutputItem + ): List { + val outputItems = ArrayList() + // we use special process with EmptyProgressIndicator to avoid changing text in our progress by inheritors search inside etc + ProgressManager.getInstance().runProcess( + { + progress?.setText("$progressText ($fileCountText) - pass $pass of 3") + + for ((i, item) in inputItems.withIndex()) { + progress?.checkCanceled() + progress?.setFraction(fraction + fractionPortion * i / fileCount) + + progress?.setText2(files!![i].getVirtualFile().getPresentableUrl()) + + outputItems.add(processItem(item)) + } + + pass++ + fraction += fractionPortion + }, + EmptyProgressIndicator()) + return outputItems + } + } + private class ProgressPortionReporter( indicator: ProgressIndicator, private val start: Double, diff --git a/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinTranslator.kt b/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinTranslator.kt index e695af4badc..a12b0c8b06a 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinTranslator.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/JavaToKotlinTranslator.kt @@ -70,8 +70,8 @@ public object JavaToKotlinTranslator { public fun generateKotlinCode(javaCode: String, project: Project): String { val file = createFile(javaCode, project) if (file is PsiJavaFile) { - val converter = JavaToKotlinConverter(file.getProject(), ConverterSettings.defaultSettings, EmptyReferenceSearcher, EmptyResolverForConverter, null) - return prettify(converter.elementsToKotlin(listOf(JavaToKotlinConverter.InputElement(file, null))).results.single()!!.text) + val converter = JavaToKotlinConverter(file.getProject(), ConverterSettings.defaultSettings, EmptyReferenceSearcher, EmptyResolverForConverter) + return prettify(converter.elementsToKotlin(listOf(file)).results.single()!!.text) //TODO: imports } return "" } diff --git a/j2k/src/org/jetbrains/kotlin/j2k/TypeConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/TypeConverter.kt index c7c463b5ba1..fff171d31f9 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/TypeConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/TypeConverter.kt @@ -38,10 +38,15 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.descriptors.CallableDescriptor class TypeConverter(val converter: Converter) { - public fun convertType(type: PsiType?, nullability: Nullability = Nullability.Default, mutability: Mutability = Mutability.Default): Type { + public fun convertType( + type: PsiType?, + nullability: Nullability = Nullability.Default, + mutability: Mutability = Mutability.Default, + inAnnotationType: Boolean = false + ): Type { if (type == null) return ErrorType().assignNoPrototype() - val result = type.accept(TypeVisitor(converter, type, mutability))!!.assignNoPrototype() + val result = type.accept(TypeVisitor(converter, type, mutability, inAnnotationType))!!.assignNoPrototype() return when (nullability) { Nullability.NotNull -> result.toNotNullType() Nullability.Nullable -> result.toNullableType() diff --git a/j2k/src/org/jetbrains/kotlin/j2k/TypeVisitor.kt b/j2k/src/org/jetbrains/kotlin/j2k/TypeVisitor.kt index 8dee4a03700..75c302700ef 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/TypeVisitor.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/TypeVisitor.kt @@ -16,10 +16,11 @@ package org.jetbrains.kotlin.j2k +import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.* import com.intellij.psi.impl.source.PsiClassReferenceType import org.jetbrains.kotlin.j2k.ast.* -import com.intellij.openapi.util.text.StringUtil +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType private val PRIMITIVE_TYPES_NAMES = JvmPrimitiveType.values().map { it.getName() } @@ -27,7 +28,8 @@ private val PRIMITIVE_TYPES_NAMES = JvmPrimitiveType.values().map { it.getName() class TypeVisitor( private val converter: Converter, private val topLevelType: PsiType, - private val topLevelTypeMutability: Mutability + private val topLevelTypeMutability: Mutability, + private val inAnnotationType: Boolean ) : PsiTypeVisitor() { private val typeConverter: TypeConverter = converter.typeConverter @@ -49,7 +51,7 @@ class TypeVisitor( } override fun visitArrayType(arrayType: PsiArrayType): Type { - return ArrayType(typeConverter.convertType(arrayType.getComponentType()), Nullability.Default, converter.settings) + return ArrayType(typeConverter.convertType(arrayType.getComponentType(), inAnnotationType = inAnnotationType), Nullability.Default, converter.settings) } override fun visitClassType(classType: PsiClassType): Type { @@ -69,6 +71,12 @@ class TypeVisitor( if (kotlinClassName != null) { return ReferenceElement(Identifier(getShortName(kotlinClassName)).assignNoPrototype(), typeArgs).assignNoPrototype() } + + if (inAnnotationType && javaClassName == "java.lang.Class") { + val fqName = FqName("kotlin.reflect.KClass") + val identifier = Identifier(fqName.shortName().getIdentifier(), imports = listOf(fqName)).assignNoPrototype() + return ReferenceElement(identifier, typeArgs).assignNoPrototype() + } } if (classType is PsiClassReferenceType) { @@ -78,8 +86,7 @@ class TypeVisitor( return ReferenceElement(Identifier(classType.getClassName() ?: "").assignNoPrototype(), typeArgs).assignNoPrototype() } - private fun getPackageName(className: String): String = className.substring(0, className.lastIndexOf('.')) - private fun getShortName(className: String): String = className.substring(className.lastIndexOf('.') + 1) + private fun getShortName(className: String): String = className.substringAfterLast('.', className) private fun convertTypeArgs(classType: PsiClassType): List { if (classType.getParameterCount() == 0) { @@ -112,7 +119,7 @@ class TypeVisitor( } override fun visitEllipsisType(ellipsisType: PsiEllipsisType): Type { - return VarArgType(typeConverter.convertType(ellipsisType.getComponentType())) + return VarArgType(typeConverter.convertType(ellipsisType.getComponentType(), inAnnotationType = inAnnotationType)) } companion object { diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ast/Element.kt b/j2k/src/org/jetbrains/kotlin/j2k/ast/Element.kt index 5b0f1ccbb8d..7a9a5ade433 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ast/Element.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ast/Element.kt @@ -53,7 +53,7 @@ data class CommentsAndSpacesInheritance(val blankLinesBefore: Boolean = true, fun Element.canonicalCode(): String { val builder = CodeBuilder(null) builder.append(this) - return builder.result + return builder.resultText } abstract class Element { diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ast/Identifier.kt b/j2k/src/org/jetbrains/kotlin/j2k/ast/Identifier.kt index fc3292598ad..64b38099036 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ast/Identifier.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ast/Identifier.kt @@ -16,10 +16,11 @@ package org.jetbrains.kotlin.j2k.ast -import org.jetbrains.kotlin.j2k.CodeBuilder import com.intellij.psi.PsiNameIdentifierOwner +import org.jetbrains.kotlin.j2k.CodeBuilder import org.jetbrains.kotlin.lexer.JetKeywordToken import org.jetbrains.kotlin.lexer.JetTokens +import org.jetbrains.kotlin.name.FqName fun PsiNameIdentifierOwner.declarationIdentifier(): Identifier { val name = getName() @@ -29,7 +30,8 @@ fun PsiNameIdentifierOwner.declarationIdentifier(): Identifier { class Identifier( val name: String, override val isNullable: Boolean = true, - private val quotingNeeded: Boolean = true + private val quotingNeeded: Boolean = true, + private val imports: Collection = emptyList() ) : Expression() { override val isEmpty: Boolean @@ -45,6 +47,8 @@ class Identifier( override fun generateCode(builder: CodeBuilder) { builder.append(toKotlin()) + + imports.forEach { builder.addImport(it) } } private fun quote(str: String): String = "`" + str + "`" diff --git a/j2k/testData/fileOrElement/annotations/annotationUsages.kt b/j2k/testData/fileOrElement/annotations/annotationUsages.kt index bb9814fbe2b..427753c38f1 100644 --- a/j2k/testData/fileOrElement/annotations/annotationUsages.kt +++ b/j2k/testData/fileOrElement/annotations/annotationUsages.kt @@ -6,8 +6,8 @@ Anon3(e = E.A, stringArray = array(), value = *array("a", "b")) Anon4("x", "y") Anon5(1) Anon6("x", "y") -Anon7(javaClass(), javaClass()) -Anon8(classes = array(javaClass(), javaClass())) +Anon7(String::class, StringBuilder::class) +Anon8(classes = array(String::class, StringBuilder::class)) class C { Anon5(1) deprecated("") private val field1 = 0 diff --git a/j2k/testData/fileOrElement/annotations/javaClassArgument.java b/j2k/testData/fileOrElement/annotations/javaClassArgument.java new file mode 100644 index 00000000000..1bc8a405c75 --- /dev/null +++ b/j2k/testData/fileOrElement/annotations/javaClassArgument.java @@ -0,0 +1,8 @@ +@interface Ann { + Class value(); + Class other(); +} + +@Ann(other = String.class, value = Object.class) +class C { +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/annotations/javaClassArgument.kt b/j2k/testData/fileOrElement/annotations/javaClassArgument.kt new file mode 100644 index 00000000000..0df37436724 --- /dev/null +++ b/j2k/testData/fileOrElement/annotations/javaClassArgument.kt @@ -0,0 +1,6 @@ +import kotlin.reflect.KClass + +annotation class Ann(public val value: KClass<*>, public val other: KClass<*>) + +Ann(other = String::class, value = Any::class) +class C \ No newline at end of file diff --git a/j2k/testData/fileOrElement/annotations/javaClassArrayArgument.java b/j2k/testData/fileOrElement/annotations/javaClassArrayArgument.java new file mode 100644 index 00000000000..d0cf391df58 --- /dev/null +++ b/j2k/testData/fileOrElement/annotations/javaClassArrayArgument.java @@ -0,0 +1,11 @@ +@interface Ann { + Class[] value(); +} + +@Ann({String.class, Object.class}) +class C { +} + +@Ann({}) +class D { +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/annotations/javaClassArrayArgument.kt b/j2k/testData/fileOrElement/annotations/javaClassArrayArgument.kt new file mode 100644 index 00000000000..526985efc57 --- /dev/null +++ b/j2k/testData/fileOrElement/annotations/javaClassArrayArgument.kt @@ -0,0 +1,9 @@ +import kotlin.reflect.KClass + +annotation class Ann(public vararg val value: KClass<*>) + +Ann(String::class, Any::class) +class C + +Ann +class D diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterMultiFileTest.kt b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterMultiFileTest.kt index 8c2705159b2..19eb0ad60eb 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterMultiFileTest.kt +++ b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterMultiFileTest.kt @@ -54,10 +54,8 @@ public abstract class AbstractJavaToKotlinConverterMultiFileTest : AbstractJavaT assert(psiFile is PsiJavaFile || psiFile is JetFile) } - val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, - IdeaReferenceSearcher, IdeaResolverForConverter, J2kPostProcessor(formatCode = true)) - val inputElements = psiFilesToConvert.map { JavaToKotlinConverter.InputElement(it, it) } - val (results, externalCodeProcessor) = converter.elementsToKotlin(inputElements) + val converter = JavaToKotlinConverter(project, ConverterSettings.defaultSettings, IdeaReferenceSearcher, IdeaResolverForConverter) + val (results, externalCodeProcessor) = converter.filesToKotlin(psiFilesToConvert, J2kPostProcessor(formatCode = true)) val process = externalCodeProcessor?.prepareWriteOperation(EmptyProgressIndicator()) project.executeWriteCommand("") { process?.invoke() } @@ -67,7 +65,7 @@ public abstract class AbstractJavaToKotlinConverterMultiFileTest : AbstractJavaT val resultFiles = ArrayList() for ((i, javaFile) in psiFilesToConvert.withIndex()) { deleteFile(javaFile.getVirtualFile()) - val virtualFile = addFile(results.map { it!!.text }[i], expectedResultFile(i).getName(), "test") + val virtualFile = addFile(results[i], expectedResultFile(i).getName(), "test") resultFiles.add(psiManager.findFile(virtualFile) as JetFile) } diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterSingleFileTest.kt b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterSingleFileTest.kt index 2052562b91f..69e5deaac16 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterSingleFileTest.kt +++ b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterSingleFileTest.kt @@ -108,9 +108,8 @@ public abstract class AbstractJavaToKotlinConverterSingleFileTest : AbstractJava private fun fileToKotlin(text: String, settings: ConverterSettings, project: Project): String { val file = createJavaFile(text) val converter = JavaToKotlinConverter(project, settings, - IdeaReferenceSearcher, IdeaResolverForConverter, J2kPostProcessor(formatCode = false)) - val inputElements = listOf(JavaToKotlinConverter.InputElement(file, file)) - return converter.elementsToKotlin(inputElements).results.single()!!.text + IdeaReferenceSearcher, IdeaResolverForConverter) + return converter.filesToKotlin(listOf(file), J2kPostProcessor(formatCode = true)).results.single() } private fun methodToKotlin(text: String, settings: ConverterSettings, project: Project): String { diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java index b41dfd58182..617a8f15e03 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java @@ -73,6 +73,18 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("javaClassArgument.java") + public void testJavaClassArgument() throws Exception { + String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/javaClassArgument.java"); + doTest(fileName); + } + + @TestMetadata("javaClassArrayArgument.java") + public void testJavaClassArrayArgument() throws Exception { + String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/javaClassArrayArgument.java"); + doTest(fileName); + } + @TestMetadata("jetbrainsNotNull.java") public void testJetbrainsNotNull() throws Exception { String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/jetbrainsNotNull.java"); diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java index 8b26ef7d919..9590d77c8be 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java @@ -73,6 +73,18 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("javaClassArgument.java") + public void testJavaClassArgument() throws Exception { + String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/javaClassArgument.java"); + doTest(fileName); + } + + @TestMetadata("javaClassArrayArgument.java") + public void testJavaClassArrayArgument() throws Exception { + String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/javaClassArrayArgument.java"); + doTest(fileName); + } + @TestMetadata("jetbrainsNotNull.java") public void testJetbrainsNotNull() throws Exception { String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/annotations/jetbrainsNotNull.java");