From afe181d93e432d1dcfc7eae44963c6f0a0e0640e Mon Sep 17 00:00:00 2001 From: Kirill Bochkarev Date: Tue, 18 Feb 2025 05:16:28 +0000 Subject: [PATCH] [workspace model] IJPL-156449 Workspace code generator for K2 - IJPL-172502 k1/k2 split - three-way super search in K2; sort supers (also in K1) - address star imports (also in K1) - sort properties (also in K1) - @Child target test - add Map property in tests - fix test imports - WorkspaceModelDefaults Merge-request: IJ-MR-151255 Merged-by: Kirill Bochkarev GitOrigin-RevId: 20d498af5e2d2ccdf374a368ad78ab72c1914620 --- .idea/modules.xml | 2 + .../intellij.devkit.workspaceModel.iml | 20 +- .../k1/intellij.devkit.workspaceModel.k1.iml | 33 ++ .../intellij.devkit.workspaceModel.k1.xml | 13 + .../src/metaModel}/DescriptorExtensions.kt | 70 ++- .../k1/src/metaModel/ObjTypeConverter.kt | 39 ++ .../k1/src/metaModel/Utils.kt | 29 ++ .../metaModel/WorkspaceMetaModelBuilder.kt} | 193 +++----- .../WorkspaceMetaModelProviderImpl.kt | 25 + .../test/AllIntellijEntitiesGenerationTest.kt | 10 + .../k1/test/EntityCodeGenTest.kt | 10 + .../k2/intellij.devkit.workspaceModel.k2.iml | 48 ++ .../intellij.devkit.workspaceModel.k2.xml | 13 + .../k2/src/metaModel/KaExtensions.kt | 143 ++++++ .../k2/src/metaModel/ObjTypeConverter.kt | 27 ++ .../k2/src/metaModel/Utils.kt | 9 + .../metaModel/WorkspaceMetaModelBuilder.kt | 311 +++++++++++++ .../WorkspaceMetaModelProviderImpl.kt | 49 ++ .../test/AllIntellijEntitiesGenerationTest.kt | 10 + .../k2/test/EntityCodeGenTest.kt | 10 + .../intellij.devkit.workspaceModel.xml | 3 + .../src/DevKitWorkspaceModelBundle.kt | 8 +- .../src/codegen/writer/CodeWriter.kt | 189 ++++---- .../src/codegen/writer/Imports.kt | 29 +- .../WorkspaceCodegenProblemsProvider.kt | 13 +- .../src/metaModel/Utils.kt | 15 + .../metaModel/WorkspaceMetaModelProvider.kt | 17 +- .../src/metaModel/WorkspaceModelDefaults.kt | 32 ++ .../src/metaModel/impl/fields.kt | 18 +- .../src/metaModel/impl/types.kt | 10 +- .../intellij.devkit.workspaceModel.tests.iml | 6 +- .../codeGen/defaultProperty/after/entity.kt | 2 +- .../after/gen/impl/MetadataStorageImpl.kt | 8 +- .../after/entity.kt | 96 ++++ .../impl/ChildrenCollectionFieldEntityImpl.kt | 267 +++++++++++ .../after/gen/impl/MetadataStorageImpl.kt | 41 ++ .../after/gen/impl/SimpleEntityImpl.kt | 299 ++++++++++++ .../before/entity.kt | 16 + .../entityWithCollections/after/entity.kt | 4 + .../gen/impl/CollectionFieldEntityImpl.kt | 26 +- .../after/gen/impl/MetadataStorageImpl.kt | 7 +- .../entityWithCollections/before/entity.kt | 3 +- .../after/entity.kt | 130 ++++++ .../after/gen/impl/ChildEntityType1Impl.kt | 247 ++++++++++ .../after/gen/impl/ChildEntityType2Impl.kt | 247 ++++++++++ .../after/gen/impl/EntityWithChildrenImpl.kt | 286 ++++++++++++ .../after/gen/impl/MetadataStorageImpl.kt | 46 ++ .../before/entity.kt | 20 + .../codeGen/finalProperty/after/entity.kt | 6 +- .../after/gen/impl/MetadataStorageImpl.kt | 18 +- .../testData/codeGen/imports/after/entity.kt | 108 +++++ .../gen/impl/EntityWithManyImportsImpl.kt | 308 +++++++++++++ .../after/gen/impl/MetadataStorageImpl.kt | 50 ++ .../after/gen/impl/SimpleEntityImpl.kt | 256 +++++++++++ .../testData/codeGen/imports/before/entity.kt | 33 ++ .../codeGen/propertiesOrder/after/entity.kt | 181 ++++++++ .../after/gen/impl/ChildEntityImpl.kt | 427 ++++++++++++++++++ .../after/gen/impl/MetadataStorageImpl.kt | 83 ++++ .../after/gen/impl/SimpleEntityImpl.kt | 192 ++++++++ .../codeGen/propertiesOrder/before/entity.kt | 55 +++ .../refsFromAnotherModule/after/entity.kt | 2 +- .../codeGen/symbolicId/after/entity.kt | 6 +- .../after/gen/impl/MetadataStorageImpl.kt | 38 +- .../gen/impl/SimpleSymbolicIdEntityImpl.kt | 195 +++++++- .../codeGen/symbolicId/before/entity.kt | 7 +- ...tractAllIntellijEntitiesGenerationTest.kt} | 41 +- ...enTest.kt => AbstractEntityCodeGenTest.kt} | 23 +- .../CodeGenerationTestBase.kt | 0 .../WorkspaceEntityInspectionBase.kt | 0 ...rkspaceEntityMutableFieldInspectionTest.kt | 0 .../intellij.devkit/plugin-content.yaml | 2 + .../resources/META-INF/plugin.xml | 2 + 72 files changed, 4807 insertions(+), 375 deletions(-) create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k1/intellij.devkit.workspaceModel.k1.iml create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k1/resources/intellij.devkit.workspaceModel.k1.xml rename plugins/devkit/intellij.devkit.workspaceModel/{src/metaModel/impl/extensions => k1/src/metaModel}/DescriptorExtensions.kt (65%) create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/ObjTypeConverter.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/Utils.kt rename plugins/devkit/intellij.devkit.workspaceModel/{src/metaModel/impl/WorkspaceMetaModelProviderImpl.kt => k1/src/metaModel/WorkspaceMetaModelBuilder.kt} (59%) create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/WorkspaceMetaModelProviderImpl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k1/test/AllIntellijEntitiesGenerationTest.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k1/test/EntityCodeGenTest.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k2/intellij.devkit.workspaceModel.k2.iml create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k2/resources/intellij.devkit.workspaceModel.k2.xml create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/KaExtensions.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/ObjTypeConverter.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/Utils.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/WorkspaceMetaModelBuilder.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/WorkspaceMetaModelProviderImpl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k2/test/AllIntellijEntitiesGenerationTest.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/k2/test/EntityCodeGenTest.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/Utils.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/WorkspaceModelDefaults.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/entity.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/gen/impl/ChildrenCollectionFieldEntityImpl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/gen/impl/MetadataStorageImpl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/gen/impl/SimpleEntityImpl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/before/entity.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/entity.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/ChildEntityType1Impl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/ChildEntityType2Impl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/EntityWithChildrenImpl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/MetadataStorageImpl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/before/entity.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/entity.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/gen/impl/EntityWithManyImportsImpl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/gen/impl/MetadataStorageImpl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/gen/impl/SimpleEntityImpl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/before/entity.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/entity.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/gen/impl/ChildEntityImpl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/gen/impl/MetadataStorageImpl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/gen/impl/SimpleEntityImpl.kt create mode 100644 plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/before/entity.kt rename plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/{com/intellij/devkit/workspaceModel/AllIntellijEntitiesGenerationTest.kt => AbstractAllIntellijEntitiesGenerationTest.kt} (93%) rename plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/{com/intellij/devkit/workspaceModel/EntityCodeGenTest.kt => AbstractEntityCodeGenTest.kt} (90%) rename plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/{com/intellij/devkit/workspaceModel => }/CodeGenerationTestBase.kt (100%) rename plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/{com/intellij/devkit/workspaceModel => }/inspections/WorkspaceEntityInspectionBase.kt (100%) rename plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/{com/intellij/devkit/workspaceModel => }/inspections/WorkspaceEntityMutableFieldInspectionTest.kt (100%) diff --git a/.idea/modules.xml b/.idea/modules.xml index 424dccfeb890..d87d1422e2ea 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -356,6 +356,8 @@ + + diff --git a/plugins/devkit/intellij.devkit.workspaceModel/intellij.devkit.workspaceModel.iml b/plugins/devkit/intellij.devkit.workspaceModel/intellij.devkit.workspaceModel.iml index 84fedb22b92d..cef7f54d19a8 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/intellij.devkit.workspaceModel.iml +++ b/plugins/devkit/intellij.devkit.workspaceModel/intellij.devkit.workspaceModel.iml @@ -4,7 +4,7 @@ - @@ -24,38 +24,38 @@ + + + + + - - - - - - - - - + + + + \ No newline at end of file diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k1/intellij.devkit.workspaceModel.k1.iml b/plugins/devkit/intellij.devkit.workspaceModel/k1/intellij.devkit.workspaceModel.k1.iml new file mode 100644 index 000000000000..40fa32898583 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k1/intellij.devkit.workspaceModel.k1.iml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k1/resources/intellij.devkit.workspaceModel.k1.xml b/plugins/devkit/intellij.devkit.workspaceModel/k1/resources/intellij.devkit.workspaceModel.k1.xml new file mode 100644 index 000000000000..3a7d850aef62 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k1/resources/intellij.devkit.workspaceModel.k1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/impl/extensions/DescriptorExtensions.kt b/plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/DescriptorExtensions.kt similarity index 65% rename from plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/impl/extensions/DescriptorExtensions.kt rename to plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/DescriptorExtensions.kt index 9c8e83fa3777..0df0a2c55ce0 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/impl/extensions/DescriptorExtensions.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/DescriptorExtensions.kt @@ -1,11 +1,13 @@ -// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.intellij.devkit.workspaceModel.metaModel.impl.extensions +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.k1.metaModel -import com.intellij.devkit.workspaceModel.metaModel.impl.* +import com.intellij.devkit.workspaceModel.metaModel.WorkspaceModelDefaults +import com.intellij.devkit.workspaceModel.metaModel.impl.CompiledObjModuleImpl +import com.intellij.devkit.workspaceModel.metaModel.impl.ObjAnnotationImpl +import com.intellij.devkit.workspaceModel.metaModel.impl.ObjClassImpl +import com.intellij.psi.CommonClassNames import com.intellij.psi.JavaPsiFacade -import com.intellij.psi.PsiClass import com.intellij.psi.search.GlobalSearchScope -import com.intellij.psi.search.SearchScope import com.intellij.psi.search.searches.ClassInheritorsSearch import com.intellij.workspaceModel.codegen.deft.meta.Obj import com.intellij.workspaceModel.codegen.deft.meta.ObjClass @@ -22,39 +24,31 @@ import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.supertypes - private val moduleAbstractTypesClassIds: List = listOf( - ClassId.topLevel(StandardNames.ENTITY_SOURCE), - ClassId.topLevel(StandardNames.SYMBOLIC_ENTITY_ID) + WorkspaceModelDefaults.ENTITY_SOURCE.classId, + WorkspaceModelDefaults.SYMBOLIC_ENTITY_ID.classId ) -private val entitiesSuperclassFqn = StandardNames.WORKSPACE_ENTITY +private val entitiesSuperclassFqn: FqName = WorkspaceModelDefaults.WORKSPACE_ENTITY.fqName -internal val blobClasses: List = arrayListOf( - StandardNames.VIRTUAL_FILE_URL, - StandardNames.ENTITY_POINTER +private val blobClasses: List = arrayListOf( + WorkspaceModelDefaults.VIRTUAL_FILE_URL.fqName, + WorkspaceModelDefaults.ENTITY_POINTER.fqName ) - internal val ModuleDescriptor.moduleAbstractTypes: List get() = moduleAbstractTypesClassIds.mapNotNull { findClassAcrossModuleDependencies(it) } internal fun ClassDescriptor.inheritors( - javaPsiFacade: JavaPsiFacade, scope: GlobalSearchScope + javaPsiFacade: JavaPsiFacade, scope: GlobalSearchScope, ): List { val psiClass = javaPsiFacade.findClass(fqNameSafe.asString(), scope) ?: return emptyList() - return psiClass.inheritors(scope).map { it.getJavaClassDescriptor()!! } + val inheritors = ClassInheritorsSearch.search(psiClass, scope, true, true, false) + .filterNot { it.qualifiedName == null } + .sortedBy { it.qualifiedName } // Sorting is needed for consistency in case of regeneration + return inheritors.map { it.getJavaClassDescriptor()!! } } -private fun PsiClass.inheritors(scope: SearchScope): List { - val inheritors = ClassInheritorsSearch.search(this, scope, true, true, false).asIterable().filterNot { it.isAnonymous } - return inheritors.sortedBy { it.qualifiedName } //Sorting is needed for consistency in case of regeneration -} - -private val PsiClass.isAnonymous: Boolean - get() = qualifiedName == null - - internal val ClassifierDescriptor.javaClassFqn: String get() { val nameWithoutPackage = fqNameSafe.asString() @@ -73,7 +67,7 @@ internal val ClassDescriptor.superTypesJavaFqns: List get() = defaultType.supertypes() .mapNotNull { it.constructor.declarationDescriptor?.javaClassFqn } .withoutStandardTypes() - + .sorted() internal val ClassDescriptor.isObject: Boolean get() = kind == ClassKind.OBJECT @@ -96,11 +90,11 @@ internal val ClassDescriptor.isAbstractClassOrInterface: Boolean get() = !isFinalClass internal val ClassDescriptor.isEntityInterface: Boolean - get() = DescriptorUtils.isInterface(this) && defaultType.isSubclassOf(entitiesSuperclassFqn) // If this is `WorkspaceEntity` interface we need to check it + // If this is `WorkspaceEntity` interface, we need to check it + get() = DescriptorUtils.isInterface(this) && defaultType.isSubclassOf(entitiesSuperclassFqn) internal val ClassDescriptor.isEntityBuilderInterface: Boolean - get() = isEntityInterface && name.identifier == "Builder" //todo improve - + get() = isEntityInterface && name.identifier == "Builder" // TODO: improve private fun KotlinType.isSubclassOf(superClassName: FqName): Boolean { return constructor.declarationDescriptor?.fqNameSafe == superClassName || constructor.supertypes.any { @@ -108,32 +102,32 @@ private fun KotlinType.isSubclassOf(superClassName: FqName): Boolean { } } - internal fun createObjTypeStub(interfaceDescriptor: ClassDescriptor, module: CompiledObjModuleImpl): ObjClassImpl { val openness = when { - interfaceDescriptor.isAnnotatedBy(StandardNames.ABSTRACT_ANNOTATION) -> ObjClass.Openness.abstract - interfaceDescriptor.isAnnotatedBy(StandardNames.OPEN_ANNOTATION) -> ObjClass.Openness.open + interfaceDescriptor.isAnnotatedBy(WorkspaceModelDefaults.ABSTRACT_ANNOTATION.fqName) -> ObjClass.Openness.abstract + interfaceDescriptor.isAnnotatedBy(WorkspaceModelDefaults.OPEN_ANNOTATION.fqName) -> ObjClass.Openness.open else -> ObjClass.Openness.final } val propertyAnnotations = interfaceDescriptor.annotations - .mapNotNull { it.fqName } - .map { ObjAnnotationImpl(it.asString(), it.pathSegments().map { segment -> segment.asString() }) } - return ObjClassImpl(module, interfaceDescriptor.name.identifier, openness, interfaceDescriptor.source, propertyAnnotations) + .mapNotNull { it.fqName } + .map { ObjAnnotationImpl(it.asString(), it.pathSegments().map { segment -> segment.asString() }) } + return ObjClassImpl(module, interfaceDescriptor.name.identifier, openness, interfaceDescriptor.source.getPsi(), propertyAnnotations) } -internal fun computeKind(property: PropertyDescriptor): ObjProperty.ValueKind { - val getter = property.getter ?: return ObjProperty.ValueKind.Plain +internal fun PropertyDescriptor.computeKind(): ObjProperty.ValueKind { + val getter = getter ?: return ObjProperty.ValueKind.Plain if (!getter.hasBody()) return ObjProperty.ValueKind.Plain val declaration = getter.source.getPsi() as? KtDeclarationWithBody ?: return ObjProperty.ValueKind.Plain val getterText = (declaration.bodyExpression ?: declaration.bodyBlockExpression)?.text return when { getterText == null -> ObjProperty.ValueKind.Plain - getter.isAnnotatedBy(StandardNames.DEFAULT_ANNOTATION) -> ObjProperty.ValueKind.WithDefault(getterText) + getter.isAnnotatedBy(WorkspaceModelDefaults.DEFAULT_ANNOTATION.fqName) -> ObjProperty.ValueKind.WithDefault(getterText) else -> ObjProperty.ValueKind.Computable(getterText) } } +private val standardTypes = setOf(Any::class.qualifiedName, CommonClassNames.JAVA_LANG_OBJECT, CommonClassNames.JAVA_LANG_ENUM) private fun Iterable.withoutStandardTypes(): List { return filterNot { standardTypes.contains(it) } -} \ No newline at end of file +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/ObjTypeConverter.kt b/plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/ObjTypeConverter.kt new file mode 100644 index 000000000000..2bf98190cbea --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/ObjTypeConverter.kt @@ -0,0 +1,39 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.k1.metaModel + +import com.intellij.psi.CommonClassNames +import com.intellij.workspaceModel.codegen.deft.meta.ValueType +import org.jetbrains.kotlin.name.FqName + +internal object ObjTypeConverter { + private val javaPrimitiveTypes = mapOf( + CommonClassNames.JAVA_LANG_BOOLEAN to ValueType.Boolean, + CommonClassNames.JAVA_LANG_BYTE to ValueType.Byte, + CommonClassNames.JAVA_LANG_SHORT to ValueType.Short, + CommonClassNames.JAVA_LANG_INTEGER to ValueType.Int, + CommonClassNames.JAVA_LANG_LONG to ValueType.Long, + CommonClassNames.JAVA_LANG_FLOAT to ValueType.Float, + CommonClassNames.JAVA_LANG_DOUBLE to ValueType.Double, + CommonClassNames.JAVA_LANG_CHARACTER to ValueType.Char, + CommonClassNames.JAVA_LANG_STRING to ValueType.String, + ) + + private val kotlinPrimitiveTypes = mapOf( + "kotlin.Boolean" to ValueType.Boolean, + "kotlin.Byte" to ValueType.Byte, + "kotlin.Short" to ValueType.Short, + "kotlin.Int" to ValueType.Int, + "kotlin.Long" to ValueType.Long, + "kotlin.Float" to ValueType.Float, + "kotlin.Double" to ValueType.Double, + "kotlin.UByte" to ValueType.UByte, + "kotlin.UShort" to ValueType.UShort, + "kotlin.UInt" to ValueType.UInt, + "kotlin.ULong" to ValueType.ULong, + "kotlin.Char" to ValueType.Char, + "kotlin.String" to ValueType.String, + ) + + operator fun get(fqName: FqName): ValueType.Primitive<*>? = + kotlinPrimitiveTypes[fqName.asString()] ?: javaPrimitiveTypes[fqName.asString()] +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/Utils.kt b/plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/Utils.kt new file mode 100644 index 000000000000..fd9369918821 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/Utils.kt @@ -0,0 +1,29 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.k1.metaModel + +import com.intellij.devkit.workspaceModel.metaModel.WorkspaceModelDefaults +import com.intellij.workspaceModel.codegen.deft.meta.CompiledObjModule +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.annotations.Annotated +import org.jetbrains.kotlin.name.FqName + +internal fun Annotated.isAnnotatedBy(fqName: FqName) = annotations.hasAnnotation(fqName) + +internal val FqName.isCollection: Boolean + get() = isList || isSet || isMap + +internal val FqName.isList: Boolean + get() = this == WorkspaceModelDefaults.LIST_INTERFACE.fqName + +internal val FqName.isSet: Boolean + get() = this == WorkspaceModelDefaults.SET_INTERFACE.fqName + +internal val FqName.isMap: Boolean + get() = this == WorkspaceModelDefaults.MAP_INTERFACE.fqName + +internal data class CompiledObjModuleAndK1Module( + val compiledObjModule: CompiledObjModule, + val kotlinModule: ModuleDescriptor, +) + +internal infix fun CompiledObjModule.and(moduleDescriptor: ModuleDescriptor) = CompiledObjModuleAndK1Module(this, moduleDescriptor) diff --git a/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/impl/WorkspaceMetaModelProviderImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/WorkspaceMetaModelBuilder.kt similarity index 59% rename from plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/impl/WorkspaceMetaModelProviderImpl.kt rename to plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/WorkspaceMetaModelBuilder.kt index 0b19b0872380..d3383c572829 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/impl/WorkspaceMetaModelProviderImpl.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/WorkspaceMetaModelBuilder.kt @@ -1,24 +1,17 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.intellij.devkit.workspaceModel.metaModel.impl +package com.intellij.devkit.workspaceModel.k1.metaModel import com.intellij.devkit.workspaceModel.metaModel.IncorrectObjInterfaceException -import com.intellij.devkit.workspaceModel.metaModel.WorkspaceMetaModelProvider -import com.intellij.devkit.workspaceModel.metaModel.impl.extensions.* +import com.intellij.devkit.workspaceModel.metaModel.WorkspaceModelDefaults +import com.intellij.devkit.workspaceModel.metaModel.impl.* +import com.intellij.devkit.workspaceModel.metaModel.unsupportedType import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project -import com.intellij.platform.workspace.storage.* -import com.intellij.platform.workspace.storage.annotations.Abstract -import com.intellij.platform.workspace.storage.annotations.Child -import com.intellij.platform.workspace.storage.annotations.Default -import com.intellij.platform.workspace.storage.annotations.Open -import com.intellij.platform.workspace.storage.url.VirtualFileUrl -import com.intellij.psi.CommonClassNames import com.intellij.psi.JavaPsiFacade import com.intellij.psi.search.GlobalSearchScope import com.intellij.workspaceModel.codegen.deft.meta.* import org.jetbrains.kotlin.caches.resolve.KotlinCacheService import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.idea.base.projectStructure.productionSourceInfo import org.jetbrains.kotlin.idea.base.projectStructure.testSourceInfo import org.jetbrains.kotlin.name.FqName @@ -26,42 +19,46 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.isExtensionProperty import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import java.util.concurrent.ConcurrentHashMap -internal class WorkspaceMetaModelProviderImpl( +internal class WorkspaceMetaModelBuilder( private val processAbstractTypes: Boolean, - project: Project -): WorkspaceMetaModelProvider { - private val objModuleByName = ConcurrentHashMap>() + project: Project, +) { + private val objModuleByName = ConcurrentHashMap() private val javaPsiFacade = JavaPsiFacade.getInstance(project) private val allScope = GlobalSearchScope.allScope(project) - override fun getObjModule(packageName: String, module: Module, isTestSourceFolder: Boolean): CompiledObjModule { - val sourceInfo = if (!isTestSourceFolder) { + fun getObjModule(packageName: String, module: Module, isTestSourceFolder: Boolean): CompiledObjModule { + val sourceInfo = if (!isTestSourceFolder) { module.productionSourceInfo - } else { + } + else { module.testSourceInfo } ?: error("No production sources in ${module.name}") - val resolutionFacade = KotlinCacheService.getInstance(module.project).getResolutionFacadeByModuleInfo(sourceInfo, sourceInfo.platform)!! + val resolutionFacade = KotlinCacheService.getInstance(module.project).getResolutionFacadeByModuleInfo(sourceInfo, sourceInfo.platform) val moduleDescriptor = resolutionFacade.moduleDescriptor return getObjModule(packageName, moduleDescriptor) } private fun getObjModule(packageName: String, moduleDescriptor: ModuleDescriptor): CompiledObjModule { val cached = objModuleByName[packageName] - if (cached != null && cached.second == moduleDescriptor) return cached.first + if (cached != null && cached.kotlinModule == moduleDescriptor) return cached.compiledObjModule val packageViewDescriptor = moduleDescriptor.getPackage(FqName(packageName)) val objModuleStub = createObjModuleStub(moduleDescriptor, packageViewDescriptor, packageName) - val result = registerObjModuleContent(packageViewDescriptor, objModuleStub, moduleDescriptor) - objModuleByName[packageName] = result to moduleDescriptor - return result + val compiledObjModule = registerObjModuleContent(packageViewDescriptor, objModuleStub, moduleDescriptor) + objModuleByName[packageName] = compiledObjModule and moduleDescriptor + return compiledObjModule } - private fun registerObjModuleContent(packageViewDescriptor: PackageViewDescriptor, - objModuleStub: ObjModuleStub, - moduleDescriptor: ModuleDescriptor): CompiledObjModule { + private fun registerObjModuleContent( + packageViewDescriptor: PackageViewDescriptor, + objModuleStub: ObjModuleStub, + moduleDescriptor: ModuleDescriptor, + ): CompiledObjModule { val extensionProperties = packageViewDescriptor.fragments.flatMap { fragment -> fragment.getMemberScope().getContributedDescriptors(DescriptorKindFilter.VARIABLES) .filterIsInstance() @@ -69,14 +66,16 @@ internal class WorkspaceMetaModelProviderImpl( .filter { it.module == moduleDescriptor } } val externalProperties = extensionProperties.mapNotNull { property -> - val receiver = property.extensionReceiverParameter?.value?.type?.constructor?.declarationDescriptor as? ClassDescriptor - receiver?.let { property to receiver } - }.filter { it.second.isEntityInterface && !it.second.isEntityBuilderInterface } + val receiver = property.extensionReceiverParameter?.value?.type?.constructor?.declarationDescriptor as? ClassDescriptor + receiver?.let { property to receiver } + }.filter { it.second.isEntityInterface && !it.second.isEntityBuilderInterface } return objModuleStub.registerContent(externalProperties) } - private fun createObjModuleStub(moduleDescriptor: ModuleDescriptor, - packageViewDescriptor: PackageViewDescriptor, packageName: String): ObjModuleStub { + private fun createObjModuleStub( + moduleDescriptor: ModuleDescriptor, + packageViewDescriptor: PackageViewDescriptor, packageName: String, + ): ObjModuleStub { val entityInterfaces = packageViewDescriptor.fragments.flatMap { fragment -> fragment.getMemberScope().getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) .filterIsInstance() @@ -99,10 +98,12 @@ internal class WorkspaceMetaModelProviderImpl( return objModule.types.find { it.name == entityInterfaceName } ?: error("Cannot find $entityInterfaceName in $objModule") } - private inner class ObjModuleStub(val module: CompiledObjModuleImpl, - val types: List>>, - val moduleAbstractTypes: List, - val moduleDescriptor: ModuleDescriptor) { + private inner class ObjModuleStub( + val module: CompiledObjModuleImpl, + val types: List>>, + val moduleAbstractTypes: List, + val moduleDescriptor: ModuleDescriptor, + ) { fun registerContent(extProperties: List>): CompiledObjModule { var extPropertyId = 0 for ((classDescriptor, objType) in types) { @@ -111,10 +112,11 @@ internal class WorkspaceMetaModelProviderImpl( .filter { it.kind.isReal } .filter { it.module == moduleDescriptor } for ((propertyId, property) in properties.withIndex()) { - val kind = computeKind(property) + val kind = property.computeKind() if (kind !is ObjProperty.ValueKind.Computable || // We can't simply skip all `Computable` because some of them are SymbolicIds - property.overriddenDescriptors.isNotEmpty()) { + property.overriddenDescriptors.isNotEmpty() + ) { objType.addField(createOwnProperty(property, propertyId, objType)) } } @@ -151,14 +153,16 @@ internal class WorkspaceMetaModelProviderImpl( } - private fun createOwnProperty(property: PropertyDescriptor, propertyId: Int, - receiver: ObjClassImpl): OwnProperty { - val valueType = convertType(property.type, hashMapOf(), property.isAnnotatedBy (StandardNames.CHILD_ANNOTATION)) + private fun createOwnProperty( + property: PropertyDescriptor, propertyId: Int, + receiver: ObjClassImpl, + ): OwnProperty { + val valueType = convertType(property.type, hashMapOf(), property.isAnnotatedBy(WorkspaceModelDefaults.CHILD_ANNOTATION.fqName)) return OwnPropertyImpl( - receiver, property.name.identifier, valueType, computeKind(property), - property.isAnnotatedBy(StandardNames.OPEN_ANNOTATION), property.isVar, + receiver, property.name.identifier, valueType, property.computeKind(), + property.isAnnotatedBy(WorkspaceModelDefaults.OPEN_ANNOTATION.fqName), property.isVar, false, false, propertyId, - property.isAnnotatedBy(StandardNames.EQUALS_BY_ANNOTATION), property.source + property.isAnnotatedBy(WorkspaceModelDefaults.EQUALS_BY_ANNOTATION.fqName), property.source.getPsi() ) } @@ -170,13 +174,17 @@ internal class WorkspaceMetaModelProviderImpl( ?: emptyList() return ExtPropertyImpl( findObjClass(receiverClass), extProperty.name.identifier, valueType, - computeKind(extProperty), extProperty.isAnnotatedBy(StandardNames.OPEN_ANNOTATION), - extProperty.isVar, false, module, extPropertyId, propertyAnnotations, extProperty.source + extProperty.computeKind(), extProperty.isAnnotatedBy(WorkspaceModelDefaults.OPEN_ANNOTATION.fqName), + extProperty.isVar, false, module, extPropertyId, propertyAnnotations, extProperty.source.getPsi() ) } - private fun convertType(type: KotlinType, knownTypes: MutableMap>, hasChildAnnotation: Boolean): ValueType<*> { + private fun convertType( + type: KotlinType, + knownTypes: MutableMap>, + hasChildAnnotation: Boolean, + ): ValueType<*> { if (type.isMarkedNullable) { return ValueType.Optional(convertType(type.makeNotNullable(), knownTypes, hasChildAnnotation)) } @@ -185,7 +193,7 @@ internal class WorkspaceMetaModelProviderImpl( if (descriptor is ClassDescriptor) { val fqName = descriptor.fqNameSafe - val primitive = ObjTypeConverter.findPrimitive(fqName) + val primitive = ObjTypeConverter[fqName] if (primitive != null) return primitive if (fqName.isCollection) { @@ -199,7 +207,8 @@ internal class WorkspaceMetaModelProviderImpl( } if (descriptor.isEntityInterface) { - return ValueType.ObjRef(type.isAnnotatedBy(StandardNames.CHILD_ANNOTATION) || hasChildAnnotation, //todo leave only one target for @Child annotation + return ValueType.ObjRef(type.isAnnotatedBy( + WorkspaceModelDefaults.CHILD_ANNOTATION.fqName) || hasChildAnnotation, //todo leave only one target for @Child annotation findObjClass(descriptor)) } return descriptor.toValueType(knownTypes, processAbstractTypes) @@ -208,7 +217,10 @@ internal class WorkspaceMetaModelProviderImpl( return unsupportedType(type.toString()) } - private fun ClassDescriptor.toValueType(knownTypes: MutableMap>, processAbstractTypes: Boolean): ValueType.JvmClass<*> { + private fun ClassDescriptor.toValueType( + knownTypes: MutableMap>, + processAbstractTypes: Boolean, + ): ValueType.JvmClass<*> { val javaClassFqn = javaClassFqn val superTypes = superTypesJavaFqns @@ -243,16 +255,21 @@ internal class WorkspaceMetaModelProviderImpl( } } - private fun createProperties(descriptor: ClassDescriptor, knownTypes: MutableMap>): List> { + private fun createProperties( + descriptor: ClassDescriptor, + knownTypes: MutableMap>, + ): List> { return descriptor.unsubstitutedMemberScope.getContributedDescriptors(DescriptorKindFilter.VARIABLES) .filterIsInstance() + .sortedBy { it.name.identifier } .map { ValueType.ClassProperty(it.name.identifier, convertType(it.type, knownTypes, false)) } } private fun findObjClass(descriptor: ClassDescriptor): ObjClass<*> { if (descriptor.packageOrDie.asString() == module.name) { - return types.find { it.first.typeConstructor == descriptor.typeConstructor }?.second ?: error("Cannot find ${descriptor.fqNameSafe} in $module") + return types.find { it.first.typeConstructor == descriptor.typeConstructor }?.second ?: error( + "Cannot find ${descriptor.fqNameSafe} in $module") } return getObjClass(descriptor) } @@ -262,77 +279,3 @@ internal class WorkspaceMetaModelProviderImpl( } } } - - -internal fun Annotated.isAnnotatedBy(fqName: FqName) = annotations.hasAnnotation(fqName) - - -private val FqName.isCollection: Boolean - get() = isList || isSet || isMap - -private val FqName.isList: Boolean - get() = this == StandardNames.LIST_INTERFACE - -private val FqName.isSet: Boolean - get() = this == StandardNames.SET_INTERFACE - -private val FqName.isMap: Boolean - get() = this == StandardNames.MAP_INTERFACE - - -private object ObjTypeConverter { - private val javaPrimitiveTypes = mapOf( - CommonClassNames.JAVA_LANG_BOOLEAN to ValueType.Boolean, - CommonClassNames.JAVA_LANG_BYTE to ValueType.Byte, - CommonClassNames.JAVA_LANG_SHORT to ValueType.Short, - CommonClassNames.JAVA_LANG_INTEGER to ValueType.Int, - CommonClassNames.JAVA_LANG_LONG to ValueType.Long, - CommonClassNames.JAVA_LANG_FLOAT to ValueType.Float, - CommonClassNames.JAVA_LANG_DOUBLE to ValueType.Double, - CommonClassNames.JAVA_LANG_CHARACTER to ValueType.Char, - CommonClassNames.JAVA_LANG_STRING to ValueType.String, - ) - - private val kotlinPrimitiveTypes = mapOf( - "kotlin.Boolean" to ValueType.Boolean, - "kotlin.Byte" to ValueType.Byte, - "kotlin.Short" to ValueType.Short, - "kotlin.Int" to ValueType.Int, - "kotlin.Long" to ValueType.Long, - "kotlin.Float" to ValueType.Float, - "kotlin.Double" to ValueType.Double, - "kotlin.UByte" to ValueType.UByte, - "kotlin.UShort" to ValueType.UShort, - "kotlin.UInt" to ValueType.UInt, - "kotlin.ULong" to ValueType.ULong, - "kotlin.Char" to ValueType.Char, - "kotlin.String" to ValueType.String, - ) - - fun findPrimitive(fqName: FqName): ValueType.Primitive<*>? = - kotlinPrimitiveTypes[fqName.asString()] ?: javaPrimitiveTypes[fqName.asString()] -} - -internal object StandardNames { - val DEFAULT_ANNOTATION = FqName(Default::class.qualifiedName!!) - val OPEN_ANNOTATION = FqName(Open::class.qualifiedName!!) - val ABSTRACT_ANNOTATION = FqName(Abstract::class.qualifiedName!!) - val CHILD_ANNOTATION = FqName(Child::class.qualifiedName!!) - val EQUALS_BY_ANNOTATION = FqName(EqualsBy::class.qualifiedName!!) - val LIST_INTERFACE = FqName(List::class.qualifiedName!!) - val SET_INTERFACE = FqName(Set::class.qualifiedName!!) - val MAP_INTERFACE = FqName(Map::class.qualifiedName!!) - - val WORKSPACE_ENTITY = FqName(WorkspaceEntity::class.qualifiedName!!) - val ENTITY_SOURCE = FqName(EntitySource::class.qualifiedName!!) - val VIRTUAL_FILE_URL = FqName(VirtualFileUrl::class.qualifiedName!!) - val SYMBOLIC_ENTITY_ID = FqName(SymbolicEntityId::class.qualifiedName!!) - val ENTITY_POINTER = FqName(EntityPointer::class.qualifiedName!!) -} - -internal val standardTypes = setOf(Any::class.qualifiedName, CommonClassNames.JAVA_LANG_OBJECT, CommonClassNames.JAVA_LANG_ENUM) - - -private fun unsupportedType(type: String?): ValueType<*> { - throw IncorrectObjInterfaceException("Unsupported type '$type'") -} \ No newline at end of file diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/WorkspaceMetaModelProviderImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/WorkspaceMetaModelProviderImpl.kt new file mode 100644 index 000000000000..4653233062ba --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k1/src/metaModel/WorkspaceMetaModelProviderImpl.kt @@ -0,0 +1,25 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.k1.metaModel + +import com.intellij.devkit.workspaceModel.metaModel.WorkspaceMetaModelProvider +import com.intellij.openapi.module.Module +import com.intellij.workspaceModel.codegen.deft.meta.CompiledObjModule +import org.jetbrains.kotlin.psi.KtClass + +internal class WorkspaceMetaModelProviderImpl : WorkspaceMetaModelProvider { + + override fun loadObjModules( + ktClasses: HashMap, + module: Module, + processAbstractTypes: Boolean, + isTestSourceFolder: Boolean, + ): List { + val packages = ktClasses.values.mapTo(LinkedHashSet()) { it.containingKtFile.packageFqName.asString() } + + val metaModelProvider = WorkspaceMetaModelBuilder( + processAbstractTypes = processAbstractTypes, + module.project + ) + return packages.filter { it != "" }.map { metaModelProvider.getObjModule(it, module, isTestSourceFolder) } + } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k1/test/AllIntellijEntitiesGenerationTest.kt b/plugins/devkit/intellij.devkit.workspaceModel/k1/test/AllIntellijEntitiesGenerationTest.kt new file mode 100644 index 000000000000..5ad671f82a30 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k1/test/AllIntellijEntitiesGenerationTest.kt @@ -0,0 +1,10 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.k1 + +import com.intellij.devkit.workspaceModel.AbstractAllIntellijEntitiesGenerationTest +import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode + +class AllIntellijEntitiesGenerationTest : AbstractAllIntellijEntitiesGenerationTest() { + override val pluginMode: KotlinPluginMode + get() = KotlinPluginMode.K1 +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k1/test/EntityCodeGenTest.kt b/plugins/devkit/intellij.devkit.workspaceModel/k1/test/EntityCodeGenTest.kt new file mode 100644 index 000000000000..d99212ecf75c --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k1/test/EntityCodeGenTest.kt @@ -0,0 +1,10 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.k1 + +import com.intellij.devkit.workspaceModel.AbstractEntityCodeGenTest +import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode + +class EntityCodeGenTest : AbstractEntityCodeGenTest() { + override val pluginMode: KotlinPluginMode + get() = KotlinPluginMode.K1 +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k2/intellij.devkit.workspaceModel.k2.iml b/plugins/devkit/intellij.devkit.workspaceModel/k2/intellij.devkit.workspaceModel.k2.iml new file mode 100644 index 000000000000..bbe5544218e0 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k2/intellij.devkit.workspaceModel.k2.iml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k2/resources/intellij.devkit.workspaceModel.k2.xml b/plugins/devkit/intellij.devkit.workspaceModel/k2/resources/intellij.devkit.workspaceModel.k2.xml new file mode 100644 index 000000000000..655a8881a737 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k2/resources/intellij.devkit.workspaceModel.k2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/KaExtensions.kt b/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/KaExtensions.kt new file mode 100644 index 000000000000..c520760313cf --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/KaExtensions.kt @@ -0,0 +1,143 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.k2.metaModel + +import com.intellij.devkit.workspaceModel.metaModel.WorkspaceModelDefaults +import com.intellij.devkit.workspaceModel.metaModel.impl.CompiledObjModuleImpl +import com.intellij.devkit.workspaceModel.metaModel.impl.ObjAnnotationImpl +import com.intellij.devkit.workspaceModel.metaModel.impl.ObjClassImpl +import com.intellij.psi.CommonClassNames +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiClass +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.search.searches.ClassInheritorsSearch +import com.intellij.workspaceModel.codegen.deft.meta.Obj +import com.intellij.workspaceModel.codegen.deft.meta.ObjClass +import com.intellij.workspaceModel.codegen.deft.meta.ObjProperty +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.annotations.KaAnnotated +import org.jetbrains.kotlin.analysis.api.symbols.* +import org.jetbrains.kotlin.analysis.api.types.symbol +import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtDeclarationWithBody + +internal fun KaAnnotated.isAnnotatedBy(classId: ClassId) = annotations.contains(classId) + +private val moduleAbstractTypesClassIds: List = listOf( + WorkspaceModelDefaults.ENTITY_SOURCE.classId, + WorkspaceModelDefaults.SYMBOLIC_ENTITY_ID.classId, +) + +internal fun KaSession.inheritors( + classSymbol: KaClassSymbol, javaPsiFacade: JavaPsiFacade, scope: GlobalSearchScope, +): List { + // can be rewritten using org.jetbrains.kotlin.idea.searching.inheritors.KotlinSearchUtilKt.findAllInheritors + val psiClass = javaPsiFacade.findClass(classSymbol.javaClassFqn, scope) ?: return emptyList() + return ClassInheritorsSearch.search(psiClass, scope, true, true, false) + .filterNot { it.isAnonymous } + .sortedBy { it.qualifiedName } // Sorting is needed for consistency in case of regeneration + .mapNotNull { + KotlinFullClassNameIndex[it.qualifiedName!!, it.project, scope].firstOrNull() + } +} + +private val standardTypes = setOf(Any::class.qualifiedName, CommonClassNames.JAVA_LANG_OBJECT, CommonClassNames.JAVA_LANG_ENUM) + +internal fun KaSession.superTypesJavaFqns( + classSymbol: KaClassSymbol, javaPsiFacade: JavaPsiFacade, scope: GlobalSearchScope, +): List { + // FIXME: There's definitely smth wrong with AA + val kaSuperTypes1 = classSymbol.defaultType.allSupertypes + .mapNotNull { it.expandedSymbol?.javaClassFqn } + .toList() + val kaSuperTypes2 = classSymbol.superTypes.flatMap { listOf(it) + it.allSupertypes } + .mapNotNull { it.expandedSymbol?.javaClassFqn } + .toList() + val psiSuperTypes = javaPsiFacade.findClass(classSymbol.javaClassFqn, scope)?.supers?.mapNotNull { it.qualifiedName } + ?: emptyList() + val mergedSuperTypes = (psiSuperTypes + kaSuperTypes1 + kaSuperTypes2).distinct() + return mergedSuperTypes.withoutStandardTypes().sorted() +} + +// TODO: merge with above? +internal val KaClassSymbol.superTypesJavaFqns: List + get() = superTypes.mapNotNull { it.symbol?.javaClassFqn }.withoutStandardTypes().sorted() + +private val PsiClass.isAnonymous: Boolean + get() = qualifiedName == null + +internal val KaClassLikeSymbol.javaClassFqn: String + get() { + val nameWithoutPackage = classId?.asSingleFqName()?.asString() + ?.substringAfter("$packageName.") + ?.replace(".", "$") + return "$packageName.$nameWithoutPackage" + } + +internal val KaClassLikeSymbol.packageName: String + get() = packageOrDie.asString() + +internal val KaClassLikeSymbol.packageOrDie: FqName + get() = classId?.packageFqName ?: error("$name has no package") + +internal fun KaSession.getPackageSymbol(classSymbol: KaClassSymbol): KaPackageSymbol? = + classSymbol.classId?.packageFqName?.let { findPackage(it) } + +private val blobClasses: List = arrayListOf( + WorkspaceModelDefaults.VIRTUAL_FILE_URL.classId, + WorkspaceModelDefaults.ENTITY_POINTER.classId, +) + +internal fun KaSession.isBlob(classSymbol: KaClassSymbol): Boolean { + val classId = classSymbol.classId ?: return false + return blobClasses.any { blobClass -> classSymbol.defaultType.isSubtypeOf(blobClass) } || + classId in moduleAbstractTypesClassIds || + classId.packageFqName.asString().startsWith("java.") || + classId.packageFqName.asString().startsWith("kotlin.") +} + +private fun Iterable.withoutStandardTypes(): List { + return filterNot { standardTypes.contains(it) } +} + +internal fun KaSession.moduleAbstractTypes(): List { + return moduleAbstractTypesClassIds.mapNotNull { classId -> findClass(classId) } +} + +internal fun KaSession.isEntityInterface(classSymbol: KaClassSymbol): Boolean = + classSymbol.classKind == KaClassKind.INTERFACE && classSymbol.defaultType.isSubtypeOf(WorkspaceModelDefaults.WORKSPACE_ENTITY.classId) + +internal fun KaSession.isEntityBuilderInterface(classSymbol: KaClassSymbol): Boolean = + isEntityInterface(classSymbol) && classSymbol.name?.identifier == "Builder" // TODO: improve + +internal fun KaSession.computeKind(property: KaPropertySymbol): ObjProperty.ValueKind { + val getter = property.getter ?: return ObjProperty.ValueKind.Plain + if (!getter.hasBody) return ObjProperty.ValueKind.Plain + val declaration = getter.psi as? KtDeclarationWithBody ?: return ObjProperty.ValueKind.Plain + val getterText = (declaration.bodyExpression ?: declaration.bodyBlockExpression)?.text + return when { + getterText == null -> ObjProperty.ValueKind.Plain + getter.isAnnotatedBy(WorkspaceModelDefaults.DEFAULT_ANNOTATION.classId) -> ObjProperty.ValueKind.WithDefault(getterText) + else -> ObjProperty.ValueKind.Computable(getterText) + } +} + +internal fun KaSession.createObjTypeStub(symbol: KaClassSymbol, module: CompiledObjModuleImpl): ObjClassImpl { + val openness = when { + symbol.isAnnotatedBy(WorkspaceModelDefaults.ABSTRACT_ANNOTATION.classId) -> ObjClass.Openness.abstract + symbol.isAnnotatedBy(WorkspaceModelDefaults.OPEN_ANNOTATION.classId) -> ObjClass.Openness.open + else -> ObjClass.Openness.final + } + + val propertyAnnotations = symbol.annotations + .mapNotNull { it.classId?.asSingleFqName() } + .map { ObjAnnotationImpl(it.asString(), it.pathSegments().map { segment -> segment.asString() }) } + + return ObjClassImpl(module, symbol.name?.identifier!!, openness, symbol.sourcePsi(), propertyAnnotations) +} + +internal fun KaSession.isChild(kaType: KaAnnotated) = + kaType.isAnnotatedBy(WorkspaceModelDefaults.CHILD_ANNOTATION.classId) diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/ObjTypeConverter.kt b/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/ObjTypeConverter.kt new file mode 100644 index 000000000000..1d4e79ab2eb0 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/ObjTypeConverter.kt @@ -0,0 +1,27 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.k2.metaModel + +import com.intellij.workspaceModel.codegen.deft.meta.ValueType +import org.jetbrains.kotlin.analysis.api.components.DefaultTypeClassIds +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.name.ClassId + +internal object ObjTypeConverter { + private val kotlinDefaultTypeToValueType = mapOf( + DefaultTypeClassIds.BOOLEAN to ValueType.Boolean, + DefaultTypeClassIds.BYTE to ValueType.Byte, + DefaultTypeClassIds.SHORT to ValueType.Short, + DefaultTypeClassIds.INT to ValueType.Int, + DefaultTypeClassIds.LONG to ValueType.Long, + DefaultTypeClassIds.FLOAT to ValueType.Float, + DefaultTypeClassIds.DOUBLE to ValueType.Double, + DefaultTypeClassIds.CHAR to ValueType.Char, + DefaultTypeClassIds.STRING to ValueType.String, + StandardNames.FqNames.uByte to ValueType.UByte, + StandardNames.FqNames.uShort to ValueType.UShort, + StandardNames.FqNames.uInt to ValueType.UInt, + StandardNames.FqNames.uLong to ValueType.ULong, + ) + + operator fun get(classId: ClassId?): ValueType.Primitive<*>? = classId?.let { kotlinDefaultTypeToValueType[it] } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/Utils.kt b/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/Utils.kt new file mode 100644 index 000000000000..bcef8639d0a3 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/Utils.kt @@ -0,0 +1,9 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.k2.metaModel + +import com.intellij.workspaceModel.codegen.deft.meta.CompiledObjModule +import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule + +internal data class CompiledObjModuleAndK2Module(val compiledObjModule: CompiledObjModule, val kotlinModule: KaModule) + +internal infix fun CompiledObjModule.and(kaModule: KaModule) = CompiledObjModuleAndK2Module(this, kaModule) diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/WorkspaceMetaModelBuilder.kt b/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/WorkspaceMetaModelBuilder.kt new file mode 100644 index 000000000000..6746ef098b93 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/WorkspaceMetaModelBuilder.kt @@ -0,0 +1,311 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.k2.metaModel + +import com.intellij.devkit.workspaceModel.metaModel.IncorrectObjInterfaceException +import com.intellij.devkit.workspaceModel.metaModel.WorkspaceModelDefaults +import com.intellij.devkit.workspaceModel.metaModel.impl.* +import com.intellij.devkit.workspaceModel.metaModel.unsupportedType +import com.intellij.openapi.project.Project +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.util.concurrency.annotations.RequiresReadLock +import com.intellij.workspaceModel.codegen.deft.meta.* +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.analyze +import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule +import org.jetbrains.kotlin.analysis.api.symbols.* +import org.jetbrains.kotlin.analysis.api.types.KaClassType +import org.jetbrains.kotlin.analysis.api.types.KaType +import org.jetbrains.kotlin.analysis.api.types.KaTypeNullability +import org.jetbrains.kotlin.idea.stubindex.KotlinExactPackagesIndex +import org.jetbrains.kotlin.name.StandardClassIds +import java.util.concurrent.ConcurrentHashMap + +internal class WorkspaceMetaModelBuilder( + private val processAbstractTypes: Boolean, + private val project: Project, +) { + private val objModuleByName = ConcurrentHashMap() + private val javaPsiFacade = JavaPsiFacade.getInstance(project) + private val allProjectScope = GlobalSearchScope.allScope(project) + + @RequiresReadLock + fun getObjModule( + packageName: String, + kaModule: KaModule, + ): CompiledObjModule = analyze(kaModule) { + val ktFile = KotlinExactPackagesIndex.get(packageName, project, kaModule.contentScope).firstOrNull() + ?: error("Cannot find any files with package $packageName in module ${kaModule}") + val packageSymbol = findPackage(ktFile.packageFqName) ?: error("Could not find package $packageName in module ${kaModule}") + getObjModule(packageName, packageSymbol, kaModule) + } + + private fun KaSession.getObjModule( + packageName: String, + packageSymbol: KaPackageSymbol, + kaModule: KaModule, + ): CompiledObjModule { + val cached = objModuleByName[packageName] + if (cached != null && cached.kotlinModule == kaModule) return cached.compiledObjModule + val objModuleStub = createObjModuleStub(packageName, packageSymbol, kaModule) + val compiledObjModule = registerObjModuleContent(packageSymbol, objModuleStub, kaModule) + objModuleByName[packageName] = compiledObjModule and kaModule + return compiledObjModule + } + + private fun KaSession.registerObjModuleContent( + packageSymbol: KaPackageSymbol, + objModuleStub: ObjModuleStub, + kaModule: KaModule, + ): CompiledObjModule { + val extensionProperties = packageSymbol.packageScope.callables + .filterIsInstance() + .filter { it.isExtension } + .filter { it.containingModule == kaModule } + .mapNotNull { propertySymbol -> + propertySymbol.receiverType?.expandedSymbol + ?.takeIf { receiverClassSymbol -> isEntityInterface(receiverClassSymbol) && !isEntityBuilderInterface(receiverClassSymbol) } + ?.let { propertySymbol to it } + } + return objModuleStub.registerContent(this@registerObjModuleContent, extensionProperties.toList()) + } + + private fun KaSession.createObjModuleStub( + packageName: String, + packageSymbol: KaPackageSymbol, + kaModule: KaModule, + ): ObjModuleStub { + val module = CompiledObjModuleImpl(packageName) + + val types = packageSymbol.packageScope.declarations + .filterIsInstance() + .filter(::isEntityInterface) + .filter { it.containingModule == kaModule } + .sortedBy { it.name } + .map { it to createObjTypeStub(it, module) } + .toList() + + val moduleAbstractTypes = moduleAbstractTypes() + return ObjModuleStub(module, types, moduleAbstractTypes, kaModule) + } + + private fun KaSession.getObjClass(entityInterface: KaClassSymbol): ObjClass<*> { + val containingPackage = getPackageSymbol(entityInterface) ?: error("Cannot find package for ${entityInterface.name ?: entityInterface}") + val objModule = getObjModule(entityInterface.packageName, containingPackage, entityInterface.containingModule) + val entityInterfaceName = entityInterface.name?.identifier ?: error("Too many errors") + return objModule.types.find { it.name == entityInterfaceName } ?: error("Cannot find $entityInterfaceName in $objModule") + } + + private inner class ObjModuleStub( + val module: CompiledObjModuleImpl, + // TODO: storing KaClassSymbol is unsafe, ObjModuleStub should inherit from KaLifetimeOwner + val types: List>>, + val moduleAbstractTypes: List, + val kaModule: KaModule, + ) /* : KaLifetimeOwner */ { + @RequiresReadLock + fun registerContent(kaSession: KaSession, extProperties: List>): CompiledObjModule = + with(kaSession) { + var extPropertyId = 0 + for ((classSymbol, objType) in types) { + val properties = classSymbol.declaredMemberScope.callables + .filterIsInstance().toList() + .filter { it.containingModule == kaModule } + + for ((propertyId, propertySymbol) in properties.withIndex()) { + val kind = computeKind(propertySymbol) + if (kind !is ObjProperty.ValueKind.Computable || + // We can't simply skip all `Computable` because some of them are SymbolicIds + // propertySymbol.overriddenDescriptors.isNotEmpty() + propertySymbol.allOverriddenSymbols.any() + ) { + objType.addField(createOwnProperty(propertySymbol, propertyId, objType)) + } + } + + classSymbol.superTypes.forEach { superType -> + val superSymbol = superType.expandedSymbol + if (superSymbol is KaClassSymbol && isEntityInterface(superSymbol)) { + val superClass = findObjClass(superSymbol) + objType.addSuperType(superClass) + } + } + + module.addType(objType) + } + + registerModuleAbstractTypes() + + for ((extProperty, receiverClass) in extProperties) { + module.addExtension(createExtProperty(extProperty, receiverClass, extPropertyId++)) + } + + module + } + + private fun KaSession.registerModuleAbstractTypes() { + for (abstractTypeClassSymbol in moduleAbstractTypes) { + val javaClassFqn = abstractTypeClassSymbol.javaClassFqn + val superTypes = abstractTypeClassSymbol.superTypesJavaFqns + val blobType = ValueType.Blob(javaClassFqn, superTypes) + + val inheritors = mutableListOf>() + val inheritorsKtClasses = inheritors(abstractTypeClassSymbol, javaPsiFacade, allProjectScope) + for (inheritor in inheritorsKtClasses) { + analyze(inheritor) { + val inheritorSymbol = inheritor.namedClassSymbol + if (inheritorSymbol != null && inheritorSymbol.packageName == module.name && inheritorSymbol.containingModule == kaModule) { + val inheritorValueType = classSymbolToValueType(inheritorSymbol, hashMapOf(javaClassFqn to blobType), true) + inheritors.add(inheritorValueType) + } + } + } + + if (inheritors.isNotEmpty()) { + module.addAbstractType(ValueType.AbstractClass(javaClassFqn, superTypes, inheritors)) + } + } + } + + private fun KaSession.createOwnProperty( + property: KaPropertySymbol, + propertyId: Int, + receiver: ObjClassImpl, + ): OwnProperty { + val valueType = convertType(property.returnType, hashMapOf(), isChild(property)) + return OwnPropertyImpl(receiver, property.name.identifier, valueType, computeKind(property), + property.isAnnotatedBy(WorkspaceModelDefaults.OPEN_ANNOTATION.classId), !property.isVal, false, false, + propertyId, property.isAnnotatedBy(WorkspaceModelDefaults.EQUALS_BY_ANNOTATION.classId), + property.sourcePsiSafe()) + } + + private fun KaSession.createExtProperty( + extProperty: KaPropertySymbol, + receiverClass: KaClassSymbol, + extPropertyId: Int, + ): ExtProperty<*, *> { + val valueType = convertType(extProperty.returnType, hashMapOf(), false) + val propertyAnnotations = extProperty.getter?.annotations?.mapNotNull { annotation -> + annotation.classId?.asSingleFqName()?.let { + ObjAnnotationImpl(it.asString(), it.pathSegments().map { segment -> segment.asString() }) + } + } ?: emptyList() + + return ExtPropertyImpl(findObjClass(receiverClass), extProperty.name.identifier, valueType, computeKind(extProperty), + extProperty.isAnnotatedBy(WorkspaceModelDefaults.OPEN_ANNOTATION.classId), !extProperty.isVal, false, module, + extPropertyId, propertyAnnotations, extProperty.sourcePsiSafe()) + } + + private fun KaSession.convertType( + type: KaType, + knownTypes: MutableMap>, + hasChildAnnotation: Boolean, + ): ValueType<*> { + if (type !is KaClassType) error("$type is not a class") + if (type.nullability == KaTypeNullability.NULLABLE) { + val nonNullableType = type.withNullability(KaTypeNullability.NON_NULLABLE) + return ValueType.Optional(convertType(nonNullableType, knownTypes, hasChildAnnotation)) + } + + val symbol = type.expandedSymbol + if (symbol is KaClassSymbol) { + val kotlinDefaultValueType = ObjTypeConverter[type.symbol.classId] + if (kotlinDefaultValueType != null) return kotlinDefaultValueType + + if (type.isSubtypeOf(StandardClassIds.Collection)) { + val typeArguments = type.typeArguments.mapNotNull { it.type } + val genericType = convertType(typeArguments.first(), knownTypes, hasChildAnnotation) + when { + type.isSubtypeOf(StandardClassIds.List) -> return ValueType.List(genericType) + type.isSubtypeOf(StandardClassIds.Set) -> return ValueType.Set(genericType) + else -> return unsupportedType(type.toString()) + } + } + + if (type.isSubtypeOf(StandardClassIds.Map)) { + val (keyType, valueType) = type.typeArguments.mapNotNull { it.type } + val convertedKeyType = convertType(keyType, knownTypes, hasChildAnnotation) + val convertedValueType = convertType(valueType, knownTypes, hasChildAnnotation) + return ValueType.Map(convertedKeyType, convertedValueType) + } + + if (symbol.classKind == KaClassKind.INTERFACE && type.isSubtypeOf(WorkspaceModelDefaults.WORKSPACE_ENTITY.classId)) { + // TODO IJPL-600 Replace @Child annotation to @Parent annotation and not for the type, but for the field + return ValueType.ObjRef(isChild(type) || hasChildAnnotation, findObjClass(symbol)) + } + + return classSymbolToValueType(symbol, knownTypes, processAbstractTypes) + } + + return unsupportedType(type.toString()) + } + + private fun KaSession.findObjClass(classSymbol: KaClassSymbol): ObjClass<*> { + if (classSymbol.packageOrDie.asString() == module.name) { + return types.find { it.first.classId == classSymbol.classId }?.second + ?: error("Cannot find ${classSymbol.name} in $module") + } + return getObjClass(classSymbol) + } + + private fun KaSession.classSymbolToValueType( + classSymbol: KaClassSymbol, + knownTypes: MutableMap>, + processAbstractTypes: Boolean, + ): ValueType.JvmClass<*> { + val javaClassFqn = classSymbol.javaClassFqn + val superTypesJavaFqns = superTypesJavaFqns(classSymbol, javaPsiFacade, allProjectScope) + + val blobType = ValueType.Blob(javaClassFqn, superTypesJavaFqns) + if (knownTypes.containsKey(javaClassFqn) || isBlob(classSymbol)) { + return blobType + } + + knownTypes[javaClassFqn] = blobType + + return when { + classSymbol.classKind == KaClassKind.OBJECT -> ValueType.Object(javaClassFqn, superTypesJavaFqns, + createProperties(classSymbol, knownTypes).toList()) + classSymbol.classKind == KaClassKind.ENUM_CLASS -> { + val enumEntries = classSymbol.staticMemberScope.callables.filterIsInstance().map { it.name.asString() } + ValueType.Enum(javaClassFqn, superTypesJavaFqns, enumEntries.toList(), + createProperties(classSymbol, knownTypes).asSequence().withoutEnumFields().toList()) + } + classSymbol.modality == KaSymbolModality.SEALED && classSymbol is KaNamedClassSymbol -> { + val subclasses = classSymbol.sealedClassInheritors + .map { convertType(it.defaultType, knownTypes, false) as ValueType.JvmClass<*> } + ValueType.AbstractClass(javaClassFqn, superTypesJavaFqns, subclasses) + } + classSymbol.classKind == KaClassKind.INTERFACE || classSymbol.modality == KaSymbolModality.ABSTRACT -> { + if (!processAbstractTypes) { + throw IncorrectObjInterfaceException("$javaClassFqn is abstract type. Abstract types are not supported in generator") + } + val inheritors = inheritors(classSymbol, javaPsiFacade, allProjectScope) + .mapNotNull { ktClassOrObject -> + analyze(ktClassOrObject) { + val inheritorSymbol = ktClassOrObject.namedClassSymbol + if (inheritorSymbol != null) { + classSymbolToValueType(inheritorSymbol, knownTypes, processAbstractTypes) + } + else null + } + } + ValueType.AbstractClass(javaClassFqn, superTypesJavaFqns, inheritors) + } + else -> ValueType.FinalClass(javaClassFqn, superTypesJavaFqns, createProperties(classSymbol, knownTypes).toList()) + } + } + + private fun KaSession.createProperties(classSymbol: KaClassSymbol, knownTypes: MutableMap>) = + classSymbol.memberScope.callables + .filterIsInstance() + .sortedBy { symbol -> symbol.name } + .map { propertySymbol -> + propertySymbol.location + ValueType.ClassProperty(propertySymbol.name.identifier, convertType(propertySymbol.returnType, knownTypes, false)) + } + } + + // TODO: Kirill does not understand why it exists + private fun Sequence>.withoutEnumFields() = filterNot { it.name == "name" || it.name == "ordinal" } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/WorkspaceMetaModelProviderImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/WorkspaceMetaModelProviderImpl.kt new file mode 100644 index 000000000000..40114f299af3 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k2/src/metaModel/WorkspaceMetaModelProviderImpl.kt @@ -0,0 +1,49 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.k2.metaModel + +import com.intellij.devkit.workspaceModel.metaModel.WorkspaceMetaModelProvider +import com.intellij.openapi.module.Module +import com.intellij.workspaceModel.codegen.deft.meta.CompiledObjModule +import org.jetbrains.kotlin.analysis.api.permissions.KaAllowAnalysisFromWriteAction +import org.jetbrains.kotlin.analysis.api.permissions.KaAllowAnalysisOnEdt +import org.jetbrains.kotlin.analysis.api.permissions.allowAnalysisFromWriteAction +import org.jetbrains.kotlin.analysis.api.permissions.allowAnalysisOnEdt +import org.jetbrains.kotlin.idea.base.projectStructure.toKaSourceModuleForProduction +import org.jetbrains.kotlin.idea.base.projectStructure.toKaSourceModuleForTest +import org.jetbrains.kotlin.psi.KtClass + +internal class WorkspaceMetaModelProviderImpl : WorkspaceMetaModelProvider { + + @OptIn(KaAllowAnalysisFromWriteAction::class, KaAllowAnalysisOnEdt::class) + override fun loadObjModules( + ktClasses: HashMap, + module: Module, + processAbstractTypes: Boolean, + isTestSourceFolder: Boolean, + ): List { + val packages = ktClasses.values.mapTo(LinkedHashSet()) { it.containingKtFile.packageFqName.asString() } + + val metaModelProvider = WorkspaceMetaModelBuilder( + processAbstractTypes, + module.project + ) + val compiledObjModule = allowAnalysisOnEdt { + allowAnalysisFromWriteAction { + packages + .filter { it != "" } + .mapNotNull { packageName -> + if (!isTestSourceFolder) { + module.toKaSourceModuleForProduction() + } + else { + module.toKaSourceModuleForTest() + } + ?.let { kaModule -> + metaModelProvider.getObjModule(packageName, kaModule) + } + } + } + } + return compiledObjModule + } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k2/test/AllIntellijEntitiesGenerationTest.kt b/plugins/devkit/intellij.devkit.workspaceModel/k2/test/AllIntellijEntitiesGenerationTest.kt new file mode 100644 index 000000000000..ed4a72dd91b6 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k2/test/AllIntellijEntitiesGenerationTest.kt @@ -0,0 +1,10 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.k2 + +import com.intellij.devkit.workspaceModel.AbstractAllIntellijEntitiesGenerationTest +import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode + +class AllIntellijEntitiesGenerationTest : AbstractAllIntellijEntitiesGenerationTest() { + override val pluginMode: KotlinPluginMode + get() = KotlinPluginMode.K2 +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/k2/test/EntityCodeGenTest.kt b/plugins/devkit/intellij.devkit.workspaceModel/k2/test/EntityCodeGenTest.kt new file mode 100644 index 000000000000..05842bdca3f0 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/k2/test/EntityCodeGenTest.kt @@ -0,0 +1,10 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.k2 + +import com.intellij.devkit.workspaceModel.AbstractEntityCodeGenTest +import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode + +class EntityCodeGenTest : AbstractEntityCodeGenTest() { + override val pluginMode: KotlinPluginMode + get() = KotlinPluginMode.K2 +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/resources/intellij.devkit.workspaceModel.xml b/plugins/devkit/intellij.devkit.workspaceModel/resources/intellij.devkit.workspaceModel.xml index abd1f39880f7..64974c18f0f3 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/resources/intellij.devkit.workspaceModel.xml +++ b/plugins/devkit/intellij.devkit.workspaceModel/resources/intellij.devkit.workspaceModel.xml @@ -1,10 +1,13 @@ + + messages.DevKitWorkspaceModelBundle + () object CodeWriter { @@ -51,7 +53,7 @@ object CodeWriter { processAbstractTypes: Boolean, explicitApiEnabled: Boolean, isTestSourceFolder: Boolean, isTestModule: Boolean, targetFolderGenerator: () -> VirtualFile?, - existingTargetFolder: () -> VirtualFile? + existingTargetFolder: () -> VirtualFile?, ) { val sourceFilePerObjModule = HashMap() val ktClasses = HashMap() @@ -59,15 +61,23 @@ object CodeWriter { if (it.extension == "kt") { val ktFile = PsiManager.getInstance(project).findFile(it) as? KtFile? - ktFile?.declarations?.filterIsInstance()?.filter { clazz -> clazz.name != null }?.forEach { clazz -> - val fqName = clazz.fqName!!.asString() - val objModuleName = fqName.replace(clazz.name!!, "").substringBeforeLast(".") + ktFile?.declarations + ?.filterIsInstance() + ?.filter { it.name != null } + ?.forEach { ktClass -> + val fqName = ktClass.fqName!!.asString() + val objModuleName = fqName + .replace(ktClass.name!!, "") + .substringBeforeLast(".") - /* We find one virtual file for each module. This is necessary to find the relative path for the generated GeneratedObjModuleFile. - See [addGeneratedObjModuleFile] method */ - sourceFilePerObjModule[objModuleName] = it - ktClasses[fqName] = clazz - } + /** + * We find one virtual file for each module. + * This is necessary to find the relative path for the generated GeneratedObjModuleFile. + * See [addGeneratedObjModuleFile] method. + */ + sourceFilePerObjModule[objModuleName] = it + ktClasses[fqName] = ktClass + } } return@processFilesRecursively true } @@ -83,11 +93,12 @@ object CodeWriter { return } - CommandProcessor.getInstance().executeCommand(project, Runnable { + executeCommand(project, DevKitWorkspaceModelBundle.message("command.name.generate.code.for.workspace.entities.in", sourceFolder.name)) { val title = DevKitWorkspaceModelBundle.message("progress.title.generating.code") ApplicationManagerEx.getApplicationEx().runWriteActionWithCancellableProgressInDispatchThread(title, project, null) { indicator -> indicator.text = DevKitWorkspaceModelBundle.message("progress.text.collecting.classes.metadata") - val objModules = loadObjModules(ktClasses, module, processAbstractTypes, isTestSourceFolder) + val metaLoader: WorkspaceMetaModelProvider = service() + val objModules = metaLoader.loadObjModules(ktClasses, module, processAbstractTypes, isTestSourceFolder) val results = generate(codeGenerator, objModules, explicitApiEnabled, isTestModule) val generatedCode = results.flatMap { it.generatedCode } @@ -120,12 +131,6 @@ object CodeWriter { indicator.text = DevKitWorkspaceModelBundle.message("progress.text.writing.code") indicator.isIndeterminate = false - val settings = CodeStyle.getSettings(project) - val kotlinSettings = settings.getCustomSettings(KotlinCodeStyleSettings::class.java) - val oldValue = kotlinSettings.NAME_COUNT_TO_USE_STAR_IMPORT - kotlinSettings.NAME_COUNT_TO_USE_STAR_IMPORT = Int.MAX_VALUE - CodeStyleSettingsManager.getInstance(project).notifyCodeStyleSettingsChanged() - generatedCode.forEachIndexed { i, code -> val psiFactory = KtPsiFactory(project) indicator.fraction = 0.15 + 0.1 * i / generatedCode.size @@ -146,40 +151,47 @@ object CodeWriter { } } - indicator.text = DevKitWorkspaceModelBundle.message("progress.text.formatting.generated.code") importsByFile.forEach { (file, imports) -> - addImports(file, imports.set) + addImports(file, imports) } - generatedFiles.withoutBigFiles().forEachIndexed { i, file -> - indicator.fraction = 0.25 + 0.7 * i / generatedFiles.size - CodeStyleManager.getInstance(project).reformat(file) - } - topLevelDeclarations.entrySet().forEach { (file, placeAndDeclarations) -> - val addedElements = ArrayList() - for ((place, declarations) in placeAndDeclarations) { - var nextPlace: PsiElement = place - val newElements = ArrayList() - for (declaration in declarations) { - val added = file.addAfter(declaration, nextPlace) as KtDeclaration - newElements.add(added) - nextPlace = added + + CodeStyle.runWithLocalSettings(project, CodeStyle.getSettings(project)) { localSettings -> + localSettings.setUpCodeStyle() + generatedFiles.withoutBigFiles().forEachIndexed { i, file -> + indicator.fraction = 0.25 + 0.7 * i / generatedFiles.size + CodeStyleManager.getInstance(project).reformat(file) + file.apiFileNameForImplFile?.let { ktClasses[it] }?.containingKtFile?.let { apiFile -> copyHeaderComment(apiFile, file) } + } + topLevelDeclarations.entrySet().forEach { (file, placeAndDeclarations) -> + val addedElements = ArrayList() + for ((place, declarations) in placeAndDeclarations) { + var nextPlace: PsiElement = place + val newElements = ArrayList() + for (declaration in declarations) { + val added = file.addAfter(declaration, nextPlace) as KtDeclaration + newElements.add(added) + nextPlace = added + } + addGeneratedRegionStartComment(file, newElements.first()) + addGeneratedRegionEndComment(file, newElements.last()) + addedElements.addAll(newElements) } - addGeneratedRegionStartComment(file, newElements.first()) - addGeneratedRegionEndComment(file, newElements.last()) - addedElements.addAll(newElements) } - } - val filesWithGeneratedRegions = ktClasses.values.groupBy { it.containingFile }.toList() - filesWithGeneratedRegions.forEachIndexed { i, (file, classes) -> - indicator.fraction = 0.95 + 0.05 * i / filesWithGeneratedRegions.size - reformatCodeInGeneratedRegions(file, classes.mapNotNull { it.body?.node } + listOf(file.node)) - } + val filesWithGeneratedRegions = ktClasses.values.groupBy { it.containingFile }.toList() + filesWithGeneratedRegions.forEachIndexed { i, (file, classes) -> + indicator.fraction = 0.95 + 0.05 * i / filesWithGeneratedRegions.size + reformatCodeInGeneratedRegions(file, classes.mapNotNull { it.body?.node } + listOf(file.node)) + } - kotlinSettings.NAME_COUNT_TO_USE_STAR_IMPORT = oldValue - CodeStyleSettingsManager.getInstance(project).notifyCodeStyleSettingsChanged() + } } - }, DevKitWorkspaceModelBundle.message("command.name.generate.code.for.workspace.entities.in", sourceFolder.name), null) + } + } + + private fun CodeStyleSettings.setUpCodeStyle() { + val kotlinCustomSettings = this.kotlinCustomSettings + kotlinCustomSettings.NAME_COUNT_TO_USE_STAR_IMPORT = Int.MAX_VALUE } // Documentation for IndexNotReadyException says that it's enough to run completeJustSubmittedTasks only once @@ -205,7 +217,8 @@ object CodeWriter { val message = if (apiVersionFromDownloadedJar == CodegenApiVersion.UNKNOWN_VERSION || apiVersionInDevkit > apiVersionFromDownloadedJar) { DevKitWorkspaceModelBundle.message("notification.workspace.incompatible.codegen.api.versions.content.newer", apiVersionInDevkit, apiVersionFromDownloadedJar) - } else { + } + else { DevKitWorkspaceModelBundle.message("notification.workspace.incompatible.codegen.api.versions.content.older", apiVersionInDevkit, apiVersionFromDownloadedJar) } @@ -248,7 +261,8 @@ object CodeWriter { val apiVersion: String? try { apiVersion = readApiVersionFromFile(fileAbsolutePath) - } catch (e: IOException) { + } + catch (e: IOException) { LOG.info("Failed to read codegen-api version from file \"$fileAbsolutePath\": " + e.message) return CodegenApiVersion.UNKNOWN_VERSION } @@ -256,18 +270,10 @@ object CodeWriter { return apiVersion ?: CodegenApiVersion.UNKNOWN_VERSION } - private fun loadObjModules(ktClasses: HashMap, module: Module, processAbstractTypes: Boolean, isTestSourceFolder: Boolean): List { - val packages = ktClasses.values.mapTo(LinkedHashSet()) { it.containingKtFile.packageFqName.asString() } - - val metaModelProvider: WorkspaceMetaModelProvider = WorkspaceMetaModelProviderImpl( - processAbstractTypes = processAbstractTypes, - module.project - ) - return packages.filter { it != "" }.map { metaModelProvider.getObjModule(it, module, isTestSourceFolder) } - } - - private fun generate(codeGenerator: CodeGenerator, objModules: List, - explicitApiEnabled: Boolean, isTestModule: Boolean): List { + private fun generate( + codeGenerator: CodeGenerator, objModules: List, + explicitApiEnabled: Boolean, isTestModule: Boolean, + ): List { val generatorSettings = GeneratorSettings(explicitApiEnabled = explicitApiEnabled, testModeEnabled = isTestModule) val entitiesImplementations = objModules.map { codeGenerator.generateEntitiesImplementation(it, generatorSettings) } val metadataStorageImplementation = codeGenerator.generateMetadataStoragesImplementation(objModules, generatorSettings) @@ -303,10 +309,12 @@ object CodeWriter { filesToRemove.forEach { it.delete(CodeWriter) } } - private fun addGeneratedObjModuleFile(code: ObjModuleFileGeneratedCode, generatedFiles: MutableList, - project: Project, sourceFolder: VirtualFile, genFolder: VirtualFile, - sourceFilePerObjModule: Map, - importsByFile: MutableMap, psiFactory: KtPsiFactory) { + private fun addGeneratedObjModuleFile( + code: ObjModuleFileGeneratedCode, generatedFiles: MutableList, + project: Project, sourceFolder: VirtualFile, genFolder: VirtualFile, + sourceFilePerObjModule: Map, + importsByFile: MutableMap, psiFactory: KtPsiFactory, + ) { val packageFqnName = code.objModuleName val sourceFile = sourceFilePerObjModule[packageFqnName]!! @@ -323,10 +331,12 @@ object CodeWriter { importsByFile[addedFile] = implImports } - private fun addGeneratedObjClassFile(code: ObjClassGeneratedCode, generatedFiles: MutableList, - project: Project, sourceFolder: VirtualFile, genFolder: VirtualFile, - ktClasses: Map, importsByFile: MutableMap, - topLevelDeclarations: MultiMap>>, psiFactory: KtPsiFactory) { + private fun addGeneratedObjClassFile( + code: ObjClassGeneratedCode, generatedFiles: MutableList, + project: Project, sourceFolder: VirtualFile, genFolder: VirtualFile, + ktClasses: Map, importsByFile: MutableMap, + topLevelDeclarations: MultiMap>>, psiFactory: KtPsiFactory, + ) { if (code.target.name in SKIPPED_TYPES) return @@ -349,7 +359,6 @@ object CodeWriter { val implPackageFqnName = "${apiFile.packageFqName.asString()}.impl" val implImports = Imports(implPackageFqnName) val implFile = psiFactory.createFile("${code.target.name}Impl.kt", implImports.findAndRemoveFqns(implementationClassText)) - copyHeaderComment(apiFile, implFile) apiClass.containingKtFile.importDirectives.mapNotNull { it.importPath }.forEach { import -> implImports.add(import.pathStr) } @@ -381,21 +390,20 @@ object CodeWriter { } } - private fun addImports(file: KtFile, imports: Collection) { + private fun addImports(file: KtFile, imports: Imports) { val psiFactory = KtPsiFactory(file.project) val importList = file.importList ?: error("no imports in ${file.name}") - val existingImports = importList.imports.mapNotNullTo(HashSet()) { it.importedFqName?.asString() } - imports.sorted().filterNot { it in existingImports }.forEach { imported -> - val place = importList.imports.find { imported < (it.importedFqName?.asString() ?: "") } - val importDirective = psiFactory.createImportDirective(ImportPath.fromString(imported)) - if (place != null) { - val added = importList.addBefore(importDirective, place) - importList.addAfter(psiFactory.createNewLine(), added) - } - else { - val added = importList.add(importDirective) - importList.addBefore(psiFactory.createNewLine(), added) - } + importList.imports.asSequence().mapNotNull { it.importPath?.pathStr }.forEach { imports.add(it) } + if (importList.children.isNotEmpty()) { + importList.deleteChildRange(importList.firstChild, importList.lastChild) + } + imports.imports.sorted().forEach { + val importDirective = psiFactory.createImportDirective(ImportPath.fromString(it)) + importList.add(importDirective) + importList.add(psiFactory.createNewLine()) + } + if (imports.isNotEmpty()) { + importList.lastChild.delete() } } @@ -454,13 +462,19 @@ object CodeWriter { if (child.isGeneratedRegionStart) { regionStart = if (child.treePrev?.elementType == KtTokens.WHITE_SPACE) child.treePrev else child } - else if (regionStart != null && child.isGeneratedRegionEnd) { - generatedRegions.add(regionStart!! to child) + else if (child.isGeneratedRegionEnd) { + regionStart?.let { generatedRegions.add(it to child) } } } return generatedRegions } + private val KtFile.apiFileNameForImplFile: String? + get() { + val packageName = packageFqName.asString() + if (!packageName.endsWith(".impl") || !name.endsWith("Impl.kt")) return null + return "${packageFqName.asString().dropLast(4)}${name.dropLast(7)}" + } private const val GENERATED_REGION_START = "//region generated code" @@ -492,9 +506,8 @@ object CodeWriter { const val UNKNOWN_VERSION = "unknown version" } - //Function was added because CodeStyleManager throws an exception for big MetadataStorageImpl files + // This function was added because CodeStyleManager throws an exception for big MetadataStorageImpl files private fun Iterable.withoutBigFiles(): Iterable { return filterNot { it.name == GENERATED_METADATA_STORAGE_FILE } } - } diff --git a/plugins/devkit/intellij.devkit.workspaceModel/src/codegen/writer/Imports.kt b/plugins/devkit/intellij.devkit.workspaceModel/src/codegen/writer/Imports.kt index 46a269f08d8d..51c4bca30e8a 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/src/codegen/writer/Imports.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/src/codegen/writer/Imports.kt @@ -19,7 +19,13 @@ private val packagesImportedByDefault = setOf( ) class Imports(private val scopeFqn: String?) { - val set = mutableSetOf() + fun isNotEmpty(): Boolean = set.isNotEmpty() || wildcardImports.isNotEmpty() + + val imports: Set + get() = set + wildcardImports.map { "$it.*" } + + private val set = mutableSetOf() + private val wildcardImports = mutableSetOf() fun findAndRemoveFqns(str: String): String { val res = StringBuilder() @@ -43,11 +49,28 @@ class Imports(private val scopeFqn: String?) { } fun add(import: String) { - set.add(import) + val importSegments = import.split('.') + if (importSegments.size < 2) set.add(import) + else { + val (packageName, name) = import + .split('.') + .let { + it.dropLast(1).joinToString(".") to it.last() + } + add(packageName, name) + } } + private fun String.packageRegex() = Regex("^${replace(".", "\\.")}\\.[^.]*$") + private fun add(packageName: String, name: String) { - if (packageName != scopeFqn && packageName !in packagesImportedByDefault) { + if (packageName == scopeFqn || packageName in packagesImportedByDefault || packageName in wildcardImports) { + return + } else if (name == "*") { + val packageRegex = packageName.packageRegex() + set.removeIf { it.matches(packageRegex) } + wildcardImports.add(packageName) + } else { set.add("$packageName.$name") } } diff --git a/plugins/devkit/intellij.devkit.workspaceModel/src/codegen/writer/WorkspaceCodegenProblemsProvider.kt b/plugins/devkit/intellij.devkit.workspaceModel/src/codegen/writer/WorkspaceCodegenProblemsProvider.kt index 06b13a7cc3ed..0e15287d9c1a 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/src/codegen/writer/WorkspaceCodegenProblemsProvider.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/src/codegen/writer/WorkspaceCodegenProblemsProvider.kt @@ -7,7 +7,7 @@ import com.intellij.analysis.problemsView.ProblemsCollector import com.intellij.analysis.problemsView.ProblemsProvider import com.intellij.analysis.problemsView.toolWindow.ProblemsViewToolWindowUtils.selectProjectErrorsTab import com.intellij.codeHighlighting.HighlightDisplayLevel -import com.intellij.devkit.workspaceModel.metaModel.ObjMetaElementWithSource +import com.intellij.devkit.workspaceModel.metaModel.ObjMetaElementWithPsi import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.Service import com.intellij.openapi.components.service @@ -18,7 +18,6 @@ import com.intellij.psi.PsiNameIdentifierOwner import com.intellij.psi.util.startOffset import com.intellij.workspaceModel.codegen.engine.GenerationProblem import com.intellij.workspaceModel.codegen.engine.ProblemLocation -import org.jetbrains.kotlin.resolve.source.getPsi import javax.swing.Icon @Service(Service.Level.PROJECT) @@ -49,11 +48,10 @@ class WorkspaceCodegenProblemsProvider(override val project: Project): ProblemsP } private fun GenerationProblem.toProblem(): WorkspaceModelCodeGenerationProblem { - val sourceElement = when (val problemLocation = location) { - is ProblemLocation.Class -> (problemLocation.objClass as ObjMetaElementWithSource).sourceElement - is ProblemLocation.Property -> (problemLocation.property as ObjMetaElementWithSource).sourceElement - } - val psiElement = sourceElement.getPsi() ?: return WorkspaceModelCodeGenerationProblem(this) + val psiElement = when (val problemLocation = location) { + is ProblemLocation.Class -> (problemLocation.objClass as ObjMetaElementWithPsi).sourcePsi + is ProblemLocation.Property -> (problemLocation.property as ObjMetaElementWithPsi).sourcePsi + } ?: return WorkspaceModelCodeGenerationProblem(this) val psiToHighlight = (psiElement as? PsiNameIdentifierOwner)?.nameIdentifier ?: psiElement val file = psiToHighlight.containingFile.virtualFile val document = FileDocumentManager.getInstance().getDocument(file) ?: return WorkspaceModelCodeGenerationProblem(this) @@ -84,4 +82,3 @@ class WorkspaceCodegenProblemsProvider(override val project: Project): ProblemsP override val column: Int ) : WorkspaceModelCodeGenerationProblem(originalProblem), FileProblem } - diff --git a/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/Utils.kt b/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/Utils.kt new file mode 100644 index 000000000000..05ab7f212453 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/Utils.kt @@ -0,0 +1,15 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.metaModel + +import com.intellij.psi.PsiElement +import com.intellij.workspaceModel.codegen.deft.meta.ValueType + +class IncorrectObjInterfaceException(errorMessage: String) : RuntimeException(errorMessage) + +interface ObjMetaElementWithPsi { + val sourcePsi: PsiElement? +} + +fun unsupportedType(type: String?): ValueType<*> { + throw IncorrectObjInterfaceException("Unsupported type '$type'") +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/WorkspaceMetaModelProvider.kt b/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/WorkspaceMetaModelProvider.kt index 939f4393bcdb..f7ee33d291a5 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/WorkspaceMetaModelProvider.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/WorkspaceMetaModelProvider.kt @@ -1,16 +1,15 @@ -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.devkit.workspaceModel.metaModel import com.intellij.openapi.module.Module import com.intellij.workspaceModel.codegen.deft.meta.CompiledObjModule -import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.psi.KtClass interface WorkspaceMetaModelProvider { - fun getObjModule(packageName: String, module: Module, isTestSourceFolder: Boolean): CompiledObjModule + fun loadObjModules( + ktClasses: HashMap, + module: Module, + processAbstractTypes: Boolean, + isTestSourceFolder: Boolean, + ): List } - -class IncorrectObjInterfaceException(errorMessage: String): RuntimeException(errorMessage) - -interface ObjMetaElementWithSource { - val sourceElement: SourceElement -} \ No newline at end of file diff --git a/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/WorkspaceModelDefaults.kt b/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/WorkspaceModelDefaults.kt new file mode 100644 index 000000000000..0d20824ddb2a --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/WorkspaceModelDefaults.kt @@ -0,0 +1,32 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.devkit.workspaceModel.metaModel + +import com.intellij.platform.workspace.storage.* +import com.intellij.platform.workspace.storage.annotations.Abstract +import com.intellij.platform.workspace.storage.annotations.Child +import com.intellij.platform.workspace.storage.annotations.Default +import com.intellij.platform.workspace.storage.annotations.Open +import com.intellij.platform.workspace.storage.url.VirtualFileUrl +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.StandardClassIds + +enum class WorkspaceModelDefaults(val fqName: FqName, val classId: ClassId) { + DEFAULT_ANNOTATION(FqName(Default::class.qualifiedName!!), ClassId.topLevel(FqName(Default::class.qualifiedName!!))), + OPEN_ANNOTATION(FqName(Open::class.qualifiedName!!), ClassId.topLevel(FqName(Open::class.qualifiedName!!))), + ABSTRACT_ANNOTATION(FqName(Abstract::class.qualifiedName!!), ClassId.topLevel(FqName(Abstract::class.qualifiedName!!))), + CHILD_ANNOTATION(FqName(Child::class.qualifiedName!!), ClassId.topLevel(FqName(Child::class.qualifiedName!!))), + EQUALS_BY_ANNOTATION(FqName(EqualsBy::class.qualifiedName!!), ClassId.topLevel(FqName(EqualsBy::class.qualifiedName!!))), + + LIST_INTERFACE(StandardNames.FqNames.list, StandardClassIds.List), + SET_INTERFACE(StandardNames.FqNames.set, StandardClassIds.Set), + MAP_INTERFACE(StandardNames.FqNames.map, StandardClassIds.Map), + + WORKSPACE_ENTITY(FqName(WorkspaceEntity::class.qualifiedName!!), ClassId.topLevel(FqName(WorkspaceEntity::class.qualifiedName!!))), + ENTITY_SOURCE(FqName(EntitySource::class.qualifiedName!!), ClassId.topLevel(FqName(EntitySource::class.qualifiedName!!))), + VIRTUAL_FILE_URL(FqName(VirtualFileUrl::class.qualifiedName!!), ClassId.topLevel(FqName(VirtualFileUrl::class.qualifiedName!!))), + SYMBOLIC_ENTITY_ID(FqName(SymbolicEntityId::class.qualifiedName!!), ClassId.topLevel(FqName(SymbolicEntityId::class.qualifiedName!!))), + ENTITY_POINTER(FqName(EntityPointer::class.qualifiedName!!), ClassId.topLevel(FqName(EntityPointer::class.qualifiedName!!))) + ; +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/impl/fields.kt b/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/impl/fields.kt index cb0483513b9a..ad27c99d5b4d 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/impl/fields.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/impl/fields.kt @@ -1,9 +1,9 @@ // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.devkit.workspaceModel.metaModel.impl -import com.intellij.devkit.workspaceModel.metaModel.ObjMetaElementWithSource +import com.intellij.devkit.workspaceModel.metaModel.ObjMetaElementWithPsi +import com.intellij.psi.PsiElement import com.intellij.workspaceModel.codegen.deft.meta.* -import org.jetbrains.kotlin.descriptors.SourceElement private val fieldObjType = ObjTypeImpl>() @@ -15,8 +15,8 @@ sealed class ObjPropertyBase( override val open: Boolean, override val mutable: Boolean, override val content: Boolean, - override val sourceElement: SourceElement -) : ObjProperty, ObjMetaElementWithSource { + override val sourcePsi: PsiElement? +) : ObjProperty, ObjMetaElementWithPsi { private val mutableOverrides: MutableList> = ArrayList() private val mutableOverriddenBy: MutableList> = ArrayList() @@ -39,8 +39,8 @@ class OwnPropertyImpl( override val constructorParameter: Boolean, override val classLocalId: Int, override val isKey: Boolean, - sourceElement: SourceElement -) : ObjPropertyBase(receiver, name, valueType, valueKind, open, mutable, content, sourceElement), OwnProperty { + sourcePsi: PsiElement? +) : ObjPropertyBase(receiver, name, valueType, valueKind, open, mutable, content, sourcePsi), OwnProperty { override fun toString(): String = "$name (${receiver.name})" } @@ -56,7 +56,7 @@ class ExtPropertyImpl( override val module: ObjModule, override val moduleLocalId: Int, override val annotations: List, - sourceElement: SourceElement, -) : ObjPropertyBase(receiver, name, valueType, valueKind, open, mutable, content, sourceElement), ExtProperty { + sourcePsi: PsiElement? +) : ObjPropertyBase(receiver, name, valueType, valueKind, open, mutable, content, sourcePsi), ExtProperty { override fun toString(): String = "$name (${receiver.name})" -} \ No newline at end of file +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/impl/types.kt b/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/impl/types.kt index a5fbd5f39dd1..8a46f8cc6c9a 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/impl/types.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/src/metaModel/impl/types.kt @@ -1,9 +1,9 @@ // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.devkit.workspaceModel.metaModel.impl -import com.intellij.devkit.workspaceModel.metaModel.ObjMetaElementWithSource +import com.intellij.devkit.workspaceModel.metaModel.ObjMetaElementWithPsi +import com.intellij.psi.PsiElement import com.intellij.workspaceModel.codegen.deft.meta.* -import org.jetbrains.kotlin.descriptors.SourceElement object ObjTypeType : ObjType> { override val classifier: ObjType> @@ -27,9 +27,9 @@ class ObjClassImpl( override val module: ObjModule, override val name: String, override val openness: ObjClass.Openness, - override val sourceElement: SourceElement, + override val sourcePsi: PsiElement?, override val annotations: List -) : ObjClass, ObjTypeImpl(), ObjMetaElementWithSource { +) : ObjClass, ObjTypeImpl(), ObjMetaElementWithPsi { override var parentField: OwnProperty? = null override var nameField: OwnProperty? = null private val mutableFields: MutableList> = ArrayList() @@ -69,4 +69,4 @@ class ObjClassImpl( class ObjAnnotationImpl(override val fqName: String, override val pathSegments: List) : ObjAnnotation { override fun toString(): String = fqName -} \ No newline at end of file +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/intellij.devkit.workspaceModel.tests.iml b/plugins/devkit/intellij.devkit.workspaceModel/tests/intellij.devkit.workspaceModel.tests.iml index ef11a61a71ec..446fa9c51684 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/intellij.devkit.workspaceModel.tests.iml +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/intellij.devkit.workspaceModel.tests.iml @@ -3,7 +3,7 @@ - + @@ -14,8 +14,7 @@ - - + @@ -29,5 +28,6 @@ + \ No newline at end of file diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/defaultProperty/after/entity.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/defaultProperty/after/entity.kt index cf32c3ecb1f0..913ed0888f21 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/defaultProperty/after/entity.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/defaultProperty/after/entity.kt @@ -4,8 +4,8 @@ import com.intellij.platform.workspace.storage.EntitySource import com.intellij.platform.workspace.storage.EntityType import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion import com.intellij.platform.workspace.storage.MutableEntityStorage -import com.intellij.platform.workspace.storage.annotations.Default import com.intellij.platform.workspace.storage.WorkspaceEntity +import com.intellij.platform.workspace.storage.annotations.Default interface DefaultFieldEntity : WorkspaceEntity { val version: Int diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/defaultProperty/after/gen/impl/MetadataStorageImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/defaultProperty/after/gen/impl/MetadataStorageImpl.kt index 17df9c2545c8..469886a001ec 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/defaultProperty/after/gen/impl/MetadataStorageImpl.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/defaultProperty/after/gen/impl/MetadataStorageImpl.kt @@ -18,8 +18,8 @@ internal object MetadataStorageImpl: MetadataStorageBase() { typeMetadata = EntityMetadata(fqName = "com.intellij.workspaceModel.test.api.DefaultFieldEntity", entityDataFqName = "com.intellij.workspaceModel.test.api.impl.DefaultFieldEntityData", supertypes = listOf("com.intellij.platform.workspace.storage.WorkspaceEntity"), properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "entitySource", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntitySource")), withDefault = false), OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "version", valueType = primitiveTypeIntNotNullable, withDefault = false), -OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "data", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.TestData", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), -OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "description", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf())), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "data", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.TestData", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "description", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf())), withDefault = false), OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "anotherVersion", valueType = primitiveTypeIntNotNullable, withDefault = true), OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "description", valueType = primitiveTypeStringNotNullable, withDefault = true)), extProperties = listOf(), isAbstract = false) @@ -27,8 +27,8 @@ OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = } override fun initializeMetadataHash() { - addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.DefaultFieldEntity", metadataHash = 124467804) - addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.TestData", metadataHash = 589453437) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.DefaultFieldEntity", metadataHash = -1646169674) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.TestData", metadataHash = -898117917) } } diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/entity.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/entity.kt new file mode 100644 index 000000000000..077fdf9d3d46 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/entity.kt @@ -0,0 +1,96 @@ +package com.intellij.workspaceModel.test.api + +import com.intellij.platform.workspace.storage.EntitySource +import com.intellij.platform.workspace.storage.EntityType +import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion +import com.intellij.platform.workspace.storage.MutableEntityStorage +import com.intellij.platform.workspace.storage.WorkspaceEntity +import com.intellij.platform.workspace.storage.annotations.Child + +interface ChildrenCollectionFieldEntity : WorkspaceEntity { + val name: String + val childrenEntitiesCollection: List<@Child SimpleEntity> + + //region generated code + @GeneratedCodeApiVersion(3) + interface Builder : WorkspaceEntity.Builder { + override var entitySource: EntitySource + var name: String + var childrenEntitiesCollection: List + } + + companion object : EntityType() { + @JvmOverloads + @JvmStatic + @JvmName("create") + operator fun invoke( + name: String, + entitySource: EntitySource, + init: (Builder.() -> Unit)? = null, + ): Builder { + val builder = builder() + builder.name = name + builder.entitySource = entitySource + init?.invoke(builder) + return builder + } + } + //endregion +} + +//region generated code +fun MutableEntityStorage.modifyChildrenCollectionFieldEntity( + entity: ChildrenCollectionFieldEntity, + modification: ChildrenCollectionFieldEntity.Builder.() -> Unit, +): ChildrenCollectionFieldEntity { + return modifyEntity(ChildrenCollectionFieldEntity.Builder::class.java, entity, modification) +} +//endregion + +interface SimpleEntity : WorkspaceEntity { + val version: Int + val name: String + val isSimple: Boolean + val parent: ChildrenCollectionFieldEntity + + //region generated code + @GeneratedCodeApiVersion(3) + interface Builder : WorkspaceEntity.Builder { + override var entitySource: EntitySource + var version: Int + var name: String + var isSimple: Boolean + var parent: ChildrenCollectionFieldEntity.Builder + } + + companion object : EntityType() { + @JvmOverloads + @JvmStatic + @JvmName("create") + operator fun invoke( + version: Int, + name: String, + isSimple: Boolean, + entitySource: EntitySource, + init: (Builder.() -> Unit)? = null, + ): Builder { + val builder = builder() + builder.version = version + builder.name = name + builder.isSimple = isSimple + builder.entitySource = entitySource + init?.invoke(builder) + return builder + } + } + //endregion +} + +//region generated code +fun MutableEntityStorage.modifySimpleEntity( + entity: SimpleEntity, + modification: SimpleEntity.Builder.() -> Unit, +): SimpleEntity { + return modifyEntity(SimpleEntity.Builder::class.java, entity, modification) +} +//endregion diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/gen/impl/ChildrenCollectionFieldEntityImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/gen/impl/ChildrenCollectionFieldEntityImpl.kt new file mode 100644 index 000000000000..1b1d31f78130 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/gen/impl/ChildrenCollectionFieldEntityImpl.kt @@ -0,0 +1,267 @@ +package com.intellij.workspaceModel.test.api.impl + +import com.intellij.platform.workspace.storage.ConnectionId +import com.intellij.platform.workspace.storage.EntitySource +import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion +import com.intellij.platform.workspace.storage.GeneratedCodeImplVersion +import com.intellij.platform.workspace.storage.MutableEntityStorage +import com.intellij.platform.workspace.storage.WorkspaceEntity +import com.intellij.platform.workspace.storage.WorkspaceEntityInternalApi +import com.intellij.platform.workspace.storage.annotations.Child +import com.intellij.platform.workspace.storage.impl.EntityLink +import com.intellij.platform.workspace.storage.impl.ModifiableWorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityData +import com.intellij.platform.workspace.storage.impl.extractOneToManyChildren +import com.intellij.platform.workspace.storage.impl.updateOneToManyChildrenOfParent +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentation +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentationApi +import com.intellij.platform.workspace.storage.instrumentation.MutableEntityStorageInstrumentation +import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata +import com.intellij.workspaceModel.test.api.ChildrenCollectionFieldEntity +import com.intellij.workspaceModel.test.api.SimpleEntity + +@GeneratedCodeApiVersion(3) +@GeneratedCodeImplVersion(6) +@OptIn(WorkspaceEntityInternalApi::class) +internal class ChildrenCollectionFieldEntityImpl(private val dataSource: ChildrenCollectionFieldEntityData) : ChildrenCollectionFieldEntity, + WorkspaceEntityBase( + dataSource + ) { + + private companion object { + internal val CHILDRENENTITIESCOLLECTION_CONNECTION_ID: ConnectionId = ConnectionId.create( + ChildrenCollectionFieldEntity::class.java, SimpleEntity::class.java, ConnectionId.ConnectionType.ONE_TO_MANY, false + ) + + private val connections = listOf( + CHILDRENENTITIESCOLLECTION_CONNECTION_ID, + ) + + } + + override val name: String + get() { + readField("name") + return dataSource.name + } + + override val childrenEntitiesCollection: List + get() = snapshot.extractOneToManyChildren(CHILDRENENTITIESCOLLECTION_CONNECTION_ID, this)!!.toList() + + override val entitySource: EntitySource + get() { + readField("entitySource") + return dataSource.entitySource + } + + override fun connectionIdList(): List { + return connections + } + + + internal class Builder(result: ChildrenCollectionFieldEntityData?) : + ModifiableWorkspaceEntityBase(result), + ChildrenCollectionFieldEntity.Builder { + internal constructor() : this(ChildrenCollectionFieldEntityData()) + + override fun applyToBuilder(builder: MutableEntityStorage) { + if (this.diff != null) { + if (existsInBuilder(builder)) { + this.diff = builder + return + } + else { + error("Entity ChildrenCollectionFieldEntity is already created in a different builder") + } + } + + this.diff = builder + addToBuilder() + this.id = getEntityData().createEntityId() + // After adding entity data to the builder, we need to unbind it and move the control over entity data to builder + // Builder may switch to snapshot at any moment and lock entity data to modification + this.currentEntityData = null + + // Process linked entities that are connected without a builder + processLinkedEntities(builder) + checkInitialization() // TODO uncomment and check failed tests + } + + private fun checkInitialization() { + val _diff = diff + if (!getEntityData().isEntitySourceInitialized()) { + error("Field WorkspaceEntity#entitySource should be initialized") + } + if (!getEntityData().isNameInitialized()) { + error("Field ChildrenCollectionFieldEntity#name should be initialized") + } + // Check initialization for list with ref type + if (_diff != null) { + if (_diff.extractOneToManyChildren(CHILDRENENTITIESCOLLECTION_CONNECTION_ID, this) == null) { + error("Field ChildrenCollectionFieldEntity#childrenEntitiesCollection should be initialized") + } + } + else { + if (this.entityLinks[EntityLink(true, CHILDRENENTITIESCOLLECTION_CONNECTION_ID)] == null) { + error("Field ChildrenCollectionFieldEntity#childrenEntitiesCollection should be initialized") + } + } + } + + override fun connectionIdList(): List { + return connections + } + + // Relabeling code, move information from dataSource to this builder + override fun relabel(dataSource: WorkspaceEntity, parents: Set?) { + dataSource as ChildrenCollectionFieldEntity + if (this.entitySource != dataSource.entitySource) this.entitySource = dataSource.entitySource + if (this.name != dataSource.name) this.name = dataSource.name + updateChildToParentReferences(parents) + } + + + override var entitySource: EntitySource + get() = getEntityData().entitySource + set(value) { + checkModificationAllowed() + getEntityData(true).entitySource = value + changedProperty.add("entitySource") + + } + + override var name: String + get() = getEntityData().name + set(value) { + checkModificationAllowed() + getEntityData(true).name = value + changedProperty.add("name") + } + + // List of non-abstract referenced types + var _childrenEntitiesCollection: List? = emptyList() + override var childrenEntitiesCollection: List + get() { + // Getter of the list of non-abstract referenced types + val _diff = diff + return if (_diff != null) { + @OptIn(EntityStorageInstrumentationApi::class) + ((_diff as MutableEntityStorageInstrumentation).getManyChildrenBuilders(CHILDRENENTITIESCOLLECTION_CONNECTION_ID, this)!! + .toList() as List) + + (this.entityLinks[EntityLink(true, CHILDRENENTITIESCOLLECTION_CONNECTION_ID)] as? List ?: emptyList()) + } + else { + this.entityLinks[EntityLink(true, CHILDRENENTITIESCOLLECTION_CONNECTION_ID)] as? List ?: emptyList() + } + } + set(value) { + // Setter of the list of non-abstract referenced types + checkModificationAllowed() + val _diff = diff + if (_diff != null) { + for (item_value in value) { + if (item_value is ModifiableWorkspaceEntityBase<*, *> && (item_value as? ModifiableWorkspaceEntityBase<*, *>)?.diff == null) { + // Backref setup before adding to store + if (item_value is ModifiableWorkspaceEntityBase<*, *>) { + item_value.entityLinks[EntityLink(false, CHILDRENENTITIESCOLLECTION_CONNECTION_ID)] = this + } + // else you're attaching a new entity to an existing entity that is not modifiable + + _diff.addEntity(item_value as ModifiableWorkspaceEntityBase) + } + } + _diff.updateOneToManyChildrenOfParent(CHILDRENENTITIESCOLLECTION_CONNECTION_ID, this, value) + } + else { + for (item_value in value) { + if (item_value is ModifiableWorkspaceEntityBase<*, *>) { + item_value.entityLinks[EntityLink(false, CHILDRENENTITIESCOLLECTION_CONNECTION_ID)] = this + } + // else you're attaching a new entity to an existing entity that is not modifiable + } + + this.entityLinks[EntityLink(true, CHILDRENENTITIESCOLLECTION_CONNECTION_ID)] = value + } + changedProperty.add("childrenEntitiesCollection") + } + + override fun getEntityClass(): Class = ChildrenCollectionFieldEntity::class.java + } +} + +@OptIn(WorkspaceEntityInternalApi::class) +internal class ChildrenCollectionFieldEntityData : WorkspaceEntityData() { + lateinit var name: String + + internal fun isNameInitialized(): Boolean = ::name.isInitialized + + override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntity.Builder { + val modifiable = ChildrenCollectionFieldEntityImpl.Builder(null) + modifiable.diff = diff + modifiable.id = createEntityId() + return modifiable + } + + @OptIn(EntityStorageInstrumentationApi::class) + override fun createEntity(snapshot: EntityStorageInstrumentation): ChildrenCollectionFieldEntity { + val entityId = createEntityId() + return snapshot.initializeEntity(entityId) { + val entity = ChildrenCollectionFieldEntityImpl(this) + entity.snapshot = snapshot + entity.id = entityId + entity + } + } + + override fun getMetadata(): EntityMetadata { + return MetadataStorageImpl.getMetadataByTypeFqn("com.intellij.workspaceModel.test.api.ChildrenCollectionFieldEntity") as EntityMetadata + } + + override fun getEntityInterface(): Class { + return ChildrenCollectionFieldEntity::class.java + } + + override fun createDetachedEntity(parents: List>): WorkspaceEntity.Builder<*> { + return ChildrenCollectionFieldEntity(name, entitySource) { + } + } + + override fun getRequiredParents(): List> { + val res = mutableListOf>() + return res + } + + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as ChildrenCollectionFieldEntityData + + if (this.entitySource != other.entitySource) return false + if (this.name != other.name) return false + return true + } + + override fun equalsIgnoringEntitySource(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as ChildrenCollectionFieldEntityData + + if (this.name != other.name) return false + return true + } + + override fun hashCode(): Int { + var result = entitySource.hashCode() + result = 31 * result + name.hashCode() + return result + } + + override fun hashCodeIgnoringEntitySource(): Int { + var result = javaClass.hashCode() + result = 31 * result + name.hashCode() + return result + } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/gen/impl/MetadataStorageImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/gen/impl/MetadataStorageImpl.kt new file mode 100644 index 000000000000..9360b9969d40 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/gen/impl/MetadataStorageImpl.kt @@ -0,0 +1,41 @@ +package com.intellij.workspaceModel.test.api.impl + +import com.intellij.platform.workspace.storage.ConnectionId +import com.intellij.platform.workspace.storage.WorkspaceEntityInternalApi +import com.intellij.platform.workspace.storage.metadata.impl.MetadataStorageBase +import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata +import com.intellij.platform.workspace.storage.metadata.model.FinalClassMetadata +import com.intellij.platform.workspace.storage.metadata.model.OwnPropertyMetadata +import com.intellij.platform.workspace.storage.metadata.model.StorageTypeMetadata +import com.intellij.platform.workspace.storage.metadata.model.ValueTypeMetadata + +@OptIn(WorkspaceEntityInternalApi::class) +internal object MetadataStorageImpl: MetadataStorageBase() { + override fun initializeMetadata() { + val primitiveTypeStringNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "String") + val primitiveTypeIntNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "Int") + val primitiveTypeBooleanNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "Boolean") + + var typeMetadata: StorageTypeMetadata + + typeMetadata = EntityMetadata(fqName = "com.intellij.workspaceModel.test.api.ChildrenCollectionFieldEntity", entityDataFqName = "com.intellij.workspaceModel.test.api.impl.ChildrenCollectionFieldEntityData", supertypes = listOf("com.intellij.platform.workspace.storage.WorkspaceEntity"), properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "entitySource", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntitySource")), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "childrenEntitiesCollection", valueType = ValueTypeMetadata.EntityReference(connectionType = ConnectionId.ConnectionType.ONE_TO_MANY, entityFqName = "com.intellij.workspaceModel.test.api.SimpleEntity", isChild = true, isNullable = false), withDefault = false)), extProperties = listOf(), isAbstract = false) + + addMetadata(typeMetadata) + + typeMetadata = EntityMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleEntity", entityDataFqName = "com.intellij.workspaceModel.test.api.impl.SimpleEntityData", supertypes = listOf("com.intellij.platform.workspace.storage.WorkspaceEntity"), properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "entitySource", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntitySource")), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "version", valueType = primitiveTypeIntNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "isSimple", valueType = primitiveTypeBooleanNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "parent", valueType = ValueTypeMetadata.EntityReference(connectionType = ConnectionId.ConnectionType.ONE_TO_MANY, entityFqName = "com.intellij.workspaceModel.test.api.ChildrenCollectionFieldEntity", isChild = false, isNullable = false), withDefault = false)), extProperties = listOf(), isAbstract = false) + + addMetadata(typeMetadata) + } + + override fun initializeMetadataHash() { + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.ChildrenCollectionFieldEntity", metadataHash = -454547805) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SimpleEntity", metadataHash = -41718252) + } + +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/gen/impl/SimpleEntityImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/gen/impl/SimpleEntityImpl.kt new file mode 100644 index 000000000000..2346e23264eb --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/after/gen/impl/SimpleEntityImpl.kt @@ -0,0 +1,299 @@ +package com.intellij.workspaceModel.test.api.impl + +import com.intellij.platform.workspace.storage.ConnectionId +import com.intellij.platform.workspace.storage.EntitySource +import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion +import com.intellij.platform.workspace.storage.GeneratedCodeImplVersion +import com.intellij.platform.workspace.storage.MutableEntityStorage +import com.intellij.platform.workspace.storage.WorkspaceEntity +import com.intellij.platform.workspace.storage.WorkspaceEntityInternalApi +import com.intellij.platform.workspace.storage.annotations.Child +import com.intellij.platform.workspace.storage.impl.EntityLink +import com.intellij.platform.workspace.storage.impl.ModifiableWorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityData +import com.intellij.platform.workspace.storage.impl.extractOneToManyParent +import com.intellij.platform.workspace.storage.impl.updateOneToManyParentOfChild +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentation +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentationApi +import com.intellij.platform.workspace.storage.instrumentation.MutableEntityStorageInstrumentation +import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata +import com.intellij.workspaceModel.test.api.ChildrenCollectionFieldEntity +import com.intellij.workspaceModel.test.api.SimpleEntity + +@GeneratedCodeApiVersion(3) +@GeneratedCodeImplVersion(6) +@OptIn(WorkspaceEntityInternalApi::class) +internal class SimpleEntityImpl(private val dataSource: SimpleEntityData) : SimpleEntity, WorkspaceEntityBase(dataSource) { + + private companion object { + internal val PARENT_CONNECTION_ID: ConnectionId = ConnectionId.create( + ChildrenCollectionFieldEntity::class.java, SimpleEntity::class.java, ConnectionId.ConnectionType.ONE_TO_MANY, false + ) + + private val connections = listOf( + PARENT_CONNECTION_ID, + ) + + } + + override val version: Int + get() { + readField("version") + return dataSource.version + } + override val name: String + get() { + readField("name") + return dataSource.name + } + + override val isSimple: Boolean + get() { + readField("isSimple") + return dataSource.isSimple + } + override val parent: ChildrenCollectionFieldEntity + get() = snapshot.extractOneToManyParent(PARENT_CONNECTION_ID, this)!! + + override val entitySource: EntitySource + get() { + readField("entitySource") + return dataSource.entitySource + } + + override fun connectionIdList(): List { + return connections + } + + + internal class Builder(result: SimpleEntityData?) : ModifiableWorkspaceEntityBase(result), + SimpleEntity.Builder { + internal constructor() : this(SimpleEntityData()) + + override fun applyToBuilder(builder: MutableEntityStorage) { + if (this.diff != null) { + if (existsInBuilder(builder)) { + this.diff = builder + return + } + else { + error("Entity SimpleEntity is already created in a different builder") + } + } + + this.diff = builder + addToBuilder() + this.id = getEntityData().createEntityId() + // After adding entity data to the builder, we need to unbind it and move the control over entity data to builder + // Builder may switch to snapshot at any moment and lock entity data to modification + this.currentEntityData = null + + // Process linked entities that are connected without a builder + processLinkedEntities(builder) + checkInitialization() // TODO uncomment and check failed tests + } + + private fun checkInitialization() { + val _diff = diff + if (!getEntityData().isEntitySourceInitialized()) { + error("Field WorkspaceEntity#entitySource should be initialized") + } + if (!getEntityData().isNameInitialized()) { + error("Field SimpleEntity#name should be initialized") + } + if (_diff != null) { + if (_diff.extractOneToManyParent(PARENT_CONNECTION_ID, this) == null) { + error("Field SimpleEntity#parent should be initialized") + } + } + else { + if (this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)] == null) { + error("Field SimpleEntity#parent should be initialized") + } + } + } + + override fun connectionIdList(): List { + return connections + } + + // Relabeling code, move information from dataSource to this builder + override fun relabel(dataSource: WorkspaceEntity, parents: Set?) { + dataSource as SimpleEntity + if (this.entitySource != dataSource.entitySource) this.entitySource = dataSource.entitySource + if (this.version != dataSource.version) this.version = dataSource.version + if (this.name != dataSource.name) this.name = dataSource.name + if (this.isSimple != dataSource.isSimple) this.isSimple = dataSource.isSimple + updateChildToParentReferences(parents) + } + + + override var entitySource: EntitySource + get() = getEntityData().entitySource + set(value) { + checkModificationAllowed() + getEntityData(true).entitySource = value + changedProperty.add("entitySource") + + } + + override var version: Int + get() = getEntityData().version + set(value) { + checkModificationAllowed() + getEntityData(true).version = value + changedProperty.add("version") + } + + override var name: String + get() = getEntityData().name + set(value) { + checkModificationAllowed() + getEntityData(true).name = value + changedProperty.add("name") + } + + override var isSimple: Boolean + get() = getEntityData().isSimple + set(value) { + checkModificationAllowed() + getEntityData(true).isSimple = value + changedProperty.add("isSimple") + } + + override var parent: ChildrenCollectionFieldEntity.Builder + get() { + val _diff = diff + return if (_diff != null) { + @OptIn(EntityStorageInstrumentationApi::class) + ((_diff as MutableEntityStorageInstrumentation).getParentBuilder( + PARENT_CONNECTION_ID, this + ) as? ChildrenCollectionFieldEntity.Builder) + ?: (this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)]!! as ChildrenCollectionFieldEntity.Builder) + } + else { + this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)]!! as ChildrenCollectionFieldEntity.Builder + } + } + set(value) { + checkModificationAllowed() + val _diff = diff + if (_diff != null && value is ModifiableWorkspaceEntityBase<*, *> && value.diff == null) { + // Setting backref of the list + if (value is ModifiableWorkspaceEntityBase<*, *>) { + val data = (value.entityLinks[EntityLink(true, PARENT_CONNECTION_ID)] as? List ?: emptyList()) + this + value.entityLinks[EntityLink(true, PARENT_CONNECTION_ID)] = data + } + // else you're attaching a new entity to an existing entity that is not modifiable + _diff.addEntity(value as ModifiableWorkspaceEntityBase) + } + if (_diff != null && (value !is ModifiableWorkspaceEntityBase<*, *> || value.diff != null)) { + _diff.updateOneToManyParentOfChild(PARENT_CONNECTION_ID, this, value) + } + else { + // Setting backref of the list + if (value is ModifiableWorkspaceEntityBase<*, *>) { + val data = (value.entityLinks[EntityLink(true, PARENT_CONNECTION_ID)] as? List ?: emptyList()) + this + value.entityLinks[EntityLink(true, PARENT_CONNECTION_ID)] = data + } + // else you're attaching a new entity to an existing entity that is not modifiable + + this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)] = value + } + changedProperty.add("parent") + } + + override fun getEntityClass(): Class = SimpleEntity::class.java + } +} + +@OptIn(WorkspaceEntityInternalApi::class) +internal class SimpleEntityData : WorkspaceEntityData() { + var version: Int = 0 + lateinit var name: String + var isSimple: Boolean = false + + + internal fun isNameInitialized(): Boolean = ::name.isInitialized + + + override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntity.Builder { + val modifiable = SimpleEntityImpl.Builder(null) + modifiable.diff = diff + modifiable.id = createEntityId() + return modifiable + } + + @OptIn(EntityStorageInstrumentationApi::class) + override fun createEntity(snapshot: EntityStorageInstrumentation): SimpleEntity { + val entityId = createEntityId() + return snapshot.initializeEntity(entityId) { + val entity = SimpleEntityImpl(this) + entity.snapshot = snapshot + entity.id = entityId + entity + } + } + + override fun getMetadata(): EntityMetadata { + return MetadataStorageImpl.getMetadataByTypeFqn("com.intellij.workspaceModel.test.api.SimpleEntity") as EntityMetadata + } + + override fun getEntityInterface(): Class { + return SimpleEntity::class.java + } + + override fun createDetachedEntity(parents: List>): WorkspaceEntity.Builder<*> { + return SimpleEntity(version, name, isSimple, entitySource) { + parents.filterIsInstance().singleOrNull()?.let { this.parent = it } + } + } + + override fun getRequiredParents(): List> { + val res = mutableListOf>() + res.add(ChildrenCollectionFieldEntity::class.java) + return res + } + + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as SimpleEntityData + + if (this.entitySource != other.entitySource) return false + if (this.version != other.version) return false + if (this.name != other.name) return false + if (this.isSimple != other.isSimple) return false + return true + } + + override fun equalsIgnoringEntitySource(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as SimpleEntityData + + if (this.version != other.version) return false + if (this.name != other.name) return false + if (this.isSimple != other.isSimple) return false + return true + } + + override fun hashCode(): Int { + var result = entitySource.hashCode() + result = 31 * result + version.hashCode() + result = 31 * result + name.hashCode() + result = 31 * result + isSimple.hashCode() + return result + } + + override fun hashCodeIgnoringEntitySource(): Int { + var result = javaClass.hashCode() + result = 31 * result + version.hashCode() + result = 31 * result + name.hashCode() + result = 31 * result + isSimple.hashCode() + return result + } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/before/entity.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/before/entity.kt new file mode 100644 index 000000000000..2f88d4fa944f --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithChildrenCollection/before/entity.kt @@ -0,0 +1,16 @@ +package com.intellij.workspaceModel.test.api + +import com.intellij.platform.workspace.storage.WorkspaceEntity +import com.intellij.platform.workspace.storage.annotations.Child + +interface ChildrenCollectionFieldEntity : WorkspaceEntity { + val name: String + val childrenEntitiesCollection: List<@Child SimpleEntity> +} + +interface SimpleEntity : WorkspaceEntity { + val version: Int + val name: String + val isSimple: Boolean + val parent: ChildrenCollectionFieldEntity +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithCollections/after/entity.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithCollections/after/entity.kt index 034086df6844..453087628a5f 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithCollections/after/entity.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithCollections/after/entity.kt @@ -11,6 +11,7 @@ import com.intellij.platform.workspace.storage.impl.containers.toMutableWorkspac interface CollectionFieldEntity : WorkspaceEntity { val versions: Set val names: List + val manifestAttributes: Map //region generated code @GeneratedCodeApiVersion(3) @@ -18,6 +19,7 @@ interface CollectionFieldEntity : WorkspaceEntity { override var entitySource: EntitySource var versions: MutableSet var names: MutableList + var manifestAttributes: Map } companion object : EntityType() { @@ -27,12 +29,14 @@ interface CollectionFieldEntity : WorkspaceEntity { operator fun invoke( versions: Set, names: List, + manifestAttributes: Map, entitySource: EntitySource, init: (Builder.() -> Unit)? = null, ): Builder { val builder = builder() builder.versions = versions.toMutableWorkspaceSet() builder.names = names.toMutableWorkspaceList() + builder.manifestAttributes = manifestAttributes builder.entitySource = entitySource init?.invoke(builder) return builder diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithCollections/after/gen/impl/CollectionFieldEntityImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithCollections/after/gen/impl/CollectionFieldEntityImpl.kt index fb66c257f937..037c64a1a92d 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithCollections/after/gen/impl/CollectionFieldEntityImpl.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithCollections/after/gen/impl/CollectionFieldEntityImpl.kt @@ -45,6 +45,12 @@ internal class CollectionFieldEntityImpl(private val dataSource: CollectionField return dataSource.names } + override val manifestAttributes: Map + get() { + readField("manifestAttributes") + return dataSource.manifestAttributes + } + override val entitySource: EntitySource get() { readField("entitySource") @@ -94,6 +100,9 @@ internal class CollectionFieldEntityImpl(private val dataSource: CollectionField if (!getEntityData().isNamesInitialized()) { error("Field CollectionFieldEntity#names should be initialized") } + if (!getEntityData().isManifestAttributesInitialized()) { + error("Field CollectionFieldEntity#manifestAttributes should be initialized") + } } override fun connectionIdList(): List { @@ -117,6 +126,7 @@ internal class CollectionFieldEntityImpl(private val dataSource: CollectionField if (this.entitySource != dataSource.entitySource) this.entitySource = dataSource.entitySource if (this.versions != dataSource.versions) this.versions = dataSource.versions.toMutableSet() if (this.names != dataSource.names) this.names = dataSource.names.toMutableList() + if (this.manifestAttributes != dataSource.manifestAttributes) this.manifestAttributes = dataSource.manifestAttributes.toMutableMap() updateChildToParentReferences(parents) } @@ -174,6 +184,14 @@ internal class CollectionFieldEntityImpl(private val dataSource: CollectionField namesUpdater.invoke(value) } + override var manifestAttributes: Map + get() = getEntityData().manifestAttributes + set(value) { + checkModificationAllowed() + getEntityData(true).manifestAttributes = value + changedProperty.add("manifestAttributes") + } + override fun getEntityClass(): Class = CollectionFieldEntity::class.java } } @@ -182,9 +200,11 @@ internal class CollectionFieldEntityImpl(private val dataSource: CollectionField internal class CollectionFieldEntityData : WorkspaceEntityData() { lateinit var versions: MutableSet lateinit var names: MutableList + lateinit var manifestAttributes: Map internal fun isVersionsInitialized(): Boolean = ::versions.isInitialized internal fun isNamesInitialized(): Boolean = ::names.isInitialized + internal fun isManifestAttributesInitialized(): Boolean = ::manifestAttributes.isInitialized override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntity.Builder { val modifiable = CollectionFieldEntityImpl.Builder(null) @@ -221,7 +241,7 @@ internal class CollectionFieldEntityData : WorkspaceEntityData>): WorkspaceEntity.Builder<*> { - return CollectionFieldEntity(versions, names, entitySource) { + return CollectionFieldEntity(versions, names, manifestAttributes, entitySource) { } } @@ -239,6 +259,7 @@ internal class CollectionFieldEntityData : WorkspaceEntityData val names: List -} \ No newline at end of file + val manifestAttributes: Map +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/entity.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/entity.kt new file mode 100644 index 000000000000..a64fc3221711 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/entity.kt @@ -0,0 +1,130 @@ +package com.intellij.workspaceModel.test.api + +import com.intellij.platform.workspace.storage.EntitySource +import com.intellij.platform.workspace.storage.EntityType +import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion +import com.intellij.platform.workspace.storage.MutableEntityStorage +import com.intellij.platform.workspace.storage.WorkspaceEntity +import com.intellij.platform.workspace.storage.annotations.Child + +interface EntityWithChildren : WorkspaceEntity { + val name: String + val propertyChild: @Child ChildEntityType1? + @Child val typeChild: ChildEntityType2? + + //region generated code + @GeneratedCodeApiVersion(3) + interface Builder : WorkspaceEntity.Builder { + override var entitySource: EntitySource + var name: String + var propertyChild: ChildEntityType1.Builder? + var typeChild: ChildEntityType2.Builder? + } + + companion object : EntityType() { + @JvmOverloads + @JvmStatic + @JvmName("create") + operator fun invoke( + name: String, + entitySource: EntitySource, + init: (Builder.() -> Unit)? = null, + ): Builder { + val builder = builder() + builder.name = name + builder.entitySource = entitySource + init?.invoke(builder) + return builder + } + } + //endregion +} + +//region generated code +fun MutableEntityStorage.modifyEntityWithChildren( + entity: EntityWithChildren, + modification: EntityWithChildren.Builder.() -> Unit, +): EntityWithChildren { + return modifyEntity(EntityWithChildren.Builder::class.java, entity, modification) +} +//endregion + +interface ChildEntityType1 : WorkspaceEntity { + val version: Int + val parent: EntityWithChildren + + //region generated code + @GeneratedCodeApiVersion(3) + interface Builder : WorkspaceEntity.Builder { + override var entitySource: EntitySource + var version: Int + var parent: EntityWithChildren.Builder + } + + companion object : EntityType() { + @JvmOverloads + @JvmStatic + @JvmName("create") + operator fun invoke( + version: Int, + entitySource: EntitySource, + init: (Builder.() -> Unit)? = null, + ): Builder { + val builder = builder() + builder.version = version + builder.entitySource = entitySource + init?.invoke(builder) + return builder + } + } + //endregion +} + +//region generated code +fun MutableEntityStorage.modifyChildEntityType1( + entity: ChildEntityType1, + modification: ChildEntityType1.Builder.() -> Unit, +): ChildEntityType1 { + return modifyEntity(ChildEntityType1.Builder::class.java, entity, modification) +} +//endregion + +interface ChildEntityType2 : WorkspaceEntity { + val version: Int + val parent: EntityWithChildren + + //region generated code + @GeneratedCodeApiVersion(3) + interface Builder : WorkspaceEntity.Builder { + override var entitySource: EntitySource + var version: Int + var parent: EntityWithChildren.Builder + } + + companion object : EntityType() { + @JvmOverloads + @JvmStatic + @JvmName("create") + operator fun invoke( + version: Int, + entitySource: EntitySource, + init: (Builder.() -> Unit)? = null, + ): Builder { + val builder = builder() + builder.version = version + builder.entitySource = entitySource + init?.invoke(builder) + return builder + } + } + //endregion +} + +//region generated code +fun MutableEntityStorage.modifyChildEntityType2( + entity: ChildEntityType2, + modification: ChildEntityType2.Builder.() -> Unit, +): ChildEntityType2 { + return modifyEntity(ChildEntityType2.Builder::class.java, entity, modification) +} +//endregion diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/ChildEntityType1Impl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/ChildEntityType1Impl.kt new file mode 100644 index 000000000000..5ee5bec4bb62 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/ChildEntityType1Impl.kt @@ -0,0 +1,247 @@ +package com.intellij.workspaceModel.test.api.impl + +import com.intellij.platform.workspace.storage.ConnectionId +import com.intellij.platform.workspace.storage.EntitySource +import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion +import com.intellij.platform.workspace.storage.GeneratedCodeImplVersion +import com.intellij.platform.workspace.storage.MutableEntityStorage +import com.intellij.platform.workspace.storage.WorkspaceEntity +import com.intellij.platform.workspace.storage.WorkspaceEntityInternalApi +import com.intellij.platform.workspace.storage.annotations.Child +import com.intellij.platform.workspace.storage.impl.EntityLink +import com.intellij.platform.workspace.storage.impl.ModifiableWorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityData +import com.intellij.platform.workspace.storage.impl.extractOneToOneParent +import com.intellij.platform.workspace.storage.impl.updateOneToOneParentOfChild +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentation +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentationApi +import com.intellij.platform.workspace.storage.instrumentation.MutableEntityStorageInstrumentation +import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata +import com.intellij.workspaceModel.test.api.ChildEntityType1 +import com.intellij.workspaceModel.test.api.EntityWithChildren + +@GeneratedCodeApiVersion(3) +@GeneratedCodeImplVersion(6) +@OptIn(WorkspaceEntityInternalApi::class) +internal class ChildEntityType1Impl(private val dataSource: ChildEntityType1Data) : ChildEntityType1, WorkspaceEntityBase(dataSource) { + + private companion object { + internal val PARENT_CONNECTION_ID: ConnectionId = + ConnectionId.create(EntityWithChildren::class.java, ChildEntityType1::class.java, ConnectionId.ConnectionType.ONE_TO_ONE, false) + + private val connections = listOf( + PARENT_CONNECTION_ID, + ) + + } + + override val version: Int + get() { + readField("version") + return dataSource.version + } + override val parent: EntityWithChildren + get() = snapshot.extractOneToOneParent(PARENT_CONNECTION_ID, this)!! + + override val entitySource: EntitySource + get() { + readField("entitySource") + return dataSource.entitySource + } + + override fun connectionIdList(): List { + return connections + } + + + internal class Builder(result: ChildEntityType1Data?) : ModifiableWorkspaceEntityBase(result), + ChildEntityType1.Builder { + internal constructor() : this(ChildEntityType1Data()) + + override fun applyToBuilder(builder: MutableEntityStorage) { + if (this.diff != null) { + if (existsInBuilder(builder)) { + this.diff = builder + return + } + else { + error("Entity ChildEntityType1 is already created in a different builder") + } + } + + this.diff = builder + addToBuilder() + this.id = getEntityData().createEntityId() + // After adding entity data to the builder, we need to unbind it and move the control over entity data to builder + // Builder may switch to snapshot at any moment and lock entity data to modification + this.currentEntityData = null + + // Process linked entities that are connected without a builder + processLinkedEntities(builder) + checkInitialization() // TODO uncomment and check failed tests + } + + private fun checkInitialization() { + val _diff = diff + if (!getEntityData().isEntitySourceInitialized()) { + error("Field WorkspaceEntity#entitySource should be initialized") + } + if (_diff != null) { + if (_diff.extractOneToOneParent(PARENT_CONNECTION_ID, this) == null) { + error("Field ChildEntityType1#parent should be initialized") + } + } + else { + if (this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)] == null) { + error("Field ChildEntityType1#parent should be initialized") + } + } + } + + override fun connectionIdList(): List { + return connections + } + + // Relabeling code, move information from dataSource to this builder + override fun relabel(dataSource: WorkspaceEntity, parents: Set?) { + dataSource as ChildEntityType1 + if (this.entitySource != dataSource.entitySource) this.entitySource = dataSource.entitySource + if (this.version != dataSource.version) this.version = dataSource.version + updateChildToParentReferences(parents) + } + + + override var entitySource: EntitySource + get() = getEntityData().entitySource + set(value) { + checkModificationAllowed() + getEntityData(true).entitySource = value + changedProperty.add("entitySource") + + } + + override var version: Int + get() = getEntityData().version + set(value) { + checkModificationAllowed() + getEntityData(true).version = value + changedProperty.add("version") + } + + override var parent: EntityWithChildren.Builder + get() { + val _diff = diff + return if (_diff != null) { + @OptIn(EntityStorageInstrumentationApi::class) + ((_diff as MutableEntityStorageInstrumentation).getParentBuilder(PARENT_CONNECTION_ID, this) as? EntityWithChildren.Builder) + ?: (this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)]!! as EntityWithChildren.Builder) + } + else { + this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)]!! as EntityWithChildren.Builder + } + } + set(value) { + checkModificationAllowed() + val _diff = diff + if (_diff != null && value is ModifiableWorkspaceEntityBase<*, *> && value.diff == null) { + if (value is ModifiableWorkspaceEntityBase<*, *>) { + value.entityLinks[EntityLink(true, PARENT_CONNECTION_ID)] = this + } + // else you're attaching a new entity to an existing entity that is not modifiable + _diff.addEntity(value as ModifiableWorkspaceEntityBase) + } + if (_diff != null && (value !is ModifiableWorkspaceEntityBase<*, *> || value.diff != null)) { + _diff.updateOneToOneParentOfChild(PARENT_CONNECTION_ID, this, value) + } + else { + if (value is ModifiableWorkspaceEntityBase<*, *>) { + value.entityLinks[EntityLink(true, PARENT_CONNECTION_ID)] = this + } + // else you're attaching a new entity to an existing entity that is not modifiable + + this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)] = value + } + changedProperty.add("parent") + } + + override fun getEntityClass(): Class = ChildEntityType1::class.java + } +} + +@OptIn(WorkspaceEntityInternalApi::class) +internal class ChildEntityType1Data : WorkspaceEntityData() { + var version: Int = 0 + + + override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntity.Builder { + val modifiable = ChildEntityType1Impl.Builder(null) + modifiable.diff = diff + modifiable.id = createEntityId() + return modifiable + } + + @OptIn(EntityStorageInstrumentationApi::class) + override fun createEntity(snapshot: EntityStorageInstrumentation): ChildEntityType1 { + val entityId = createEntityId() + return snapshot.initializeEntity(entityId) { + val entity = ChildEntityType1Impl(this) + entity.snapshot = snapshot + entity.id = entityId + entity + } + } + + override fun getMetadata(): EntityMetadata { + return MetadataStorageImpl.getMetadataByTypeFqn("com.intellij.workspaceModel.test.api.ChildEntityType1") as EntityMetadata + } + + override fun getEntityInterface(): Class { + return ChildEntityType1::class.java + } + + override fun createDetachedEntity(parents: List>): WorkspaceEntity.Builder<*> { + return ChildEntityType1(version, entitySource) { + parents.filterIsInstance().singleOrNull()?.let { this.parent = it } + } + } + + override fun getRequiredParents(): List> { + val res = mutableListOf>() + res.add(EntityWithChildren::class.java) + return res + } + + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as ChildEntityType1Data + + if (this.entitySource != other.entitySource) return false + if (this.version != other.version) return false + return true + } + + override fun equalsIgnoringEntitySource(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as ChildEntityType1Data + + if (this.version != other.version) return false + return true + } + + override fun hashCode(): Int { + var result = entitySource.hashCode() + result = 31 * result + version.hashCode() + return result + } + + override fun hashCodeIgnoringEntitySource(): Int { + var result = javaClass.hashCode() + result = 31 * result + version.hashCode() + return result + } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/ChildEntityType2Impl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/ChildEntityType2Impl.kt new file mode 100644 index 000000000000..34a2cab1e487 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/ChildEntityType2Impl.kt @@ -0,0 +1,247 @@ +package com.intellij.workspaceModel.test.api.impl + +import com.intellij.platform.workspace.storage.ConnectionId +import com.intellij.platform.workspace.storage.EntitySource +import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion +import com.intellij.platform.workspace.storage.GeneratedCodeImplVersion +import com.intellij.platform.workspace.storage.MutableEntityStorage +import com.intellij.platform.workspace.storage.WorkspaceEntity +import com.intellij.platform.workspace.storage.WorkspaceEntityInternalApi +import com.intellij.platform.workspace.storage.annotations.Child +import com.intellij.platform.workspace.storage.impl.EntityLink +import com.intellij.platform.workspace.storage.impl.ModifiableWorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityData +import com.intellij.platform.workspace.storage.impl.extractOneToOneParent +import com.intellij.platform.workspace.storage.impl.updateOneToOneParentOfChild +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentation +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentationApi +import com.intellij.platform.workspace.storage.instrumentation.MutableEntityStorageInstrumentation +import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata +import com.intellij.workspaceModel.test.api.ChildEntityType2 +import com.intellij.workspaceModel.test.api.EntityWithChildren + +@GeneratedCodeApiVersion(3) +@GeneratedCodeImplVersion(6) +@OptIn(WorkspaceEntityInternalApi::class) +internal class ChildEntityType2Impl(private val dataSource: ChildEntityType2Data) : ChildEntityType2, WorkspaceEntityBase(dataSource) { + + private companion object { + internal val PARENT_CONNECTION_ID: ConnectionId = + ConnectionId.create(EntityWithChildren::class.java, ChildEntityType2::class.java, ConnectionId.ConnectionType.ONE_TO_ONE, false) + + private val connections = listOf( + PARENT_CONNECTION_ID, + ) + + } + + override val version: Int + get() { + readField("version") + return dataSource.version + } + override val parent: EntityWithChildren + get() = snapshot.extractOneToOneParent(PARENT_CONNECTION_ID, this)!! + + override val entitySource: EntitySource + get() { + readField("entitySource") + return dataSource.entitySource + } + + override fun connectionIdList(): List { + return connections + } + + + internal class Builder(result: ChildEntityType2Data?) : ModifiableWorkspaceEntityBase(result), + ChildEntityType2.Builder { + internal constructor() : this(ChildEntityType2Data()) + + override fun applyToBuilder(builder: MutableEntityStorage) { + if (this.diff != null) { + if (existsInBuilder(builder)) { + this.diff = builder + return + } + else { + error("Entity ChildEntityType2 is already created in a different builder") + } + } + + this.diff = builder + addToBuilder() + this.id = getEntityData().createEntityId() + // After adding entity data to the builder, we need to unbind it and move the control over entity data to builder + // Builder may switch to snapshot at any moment and lock entity data to modification + this.currentEntityData = null + + // Process linked entities that are connected without a builder + processLinkedEntities(builder) + checkInitialization() // TODO uncomment and check failed tests + } + + private fun checkInitialization() { + val _diff = diff + if (!getEntityData().isEntitySourceInitialized()) { + error("Field WorkspaceEntity#entitySource should be initialized") + } + if (_diff != null) { + if (_diff.extractOneToOneParent(PARENT_CONNECTION_ID, this) == null) { + error("Field ChildEntityType2#parent should be initialized") + } + } + else { + if (this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)] == null) { + error("Field ChildEntityType2#parent should be initialized") + } + } + } + + override fun connectionIdList(): List { + return connections + } + + // Relabeling code, move information from dataSource to this builder + override fun relabel(dataSource: WorkspaceEntity, parents: Set?) { + dataSource as ChildEntityType2 + if (this.entitySource != dataSource.entitySource) this.entitySource = dataSource.entitySource + if (this.version != dataSource.version) this.version = dataSource.version + updateChildToParentReferences(parents) + } + + + override var entitySource: EntitySource + get() = getEntityData().entitySource + set(value) { + checkModificationAllowed() + getEntityData(true).entitySource = value + changedProperty.add("entitySource") + + } + + override var version: Int + get() = getEntityData().version + set(value) { + checkModificationAllowed() + getEntityData(true).version = value + changedProperty.add("version") + } + + override var parent: EntityWithChildren.Builder + get() { + val _diff = diff + return if (_diff != null) { + @OptIn(EntityStorageInstrumentationApi::class) + ((_diff as MutableEntityStorageInstrumentation).getParentBuilder(PARENT_CONNECTION_ID, this) as? EntityWithChildren.Builder) + ?: (this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)]!! as EntityWithChildren.Builder) + } + else { + this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)]!! as EntityWithChildren.Builder + } + } + set(value) { + checkModificationAllowed() + val _diff = diff + if (_diff != null && value is ModifiableWorkspaceEntityBase<*, *> && value.diff == null) { + if (value is ModifiableWorkspaceEntityBase<*, *>) { + value.entityLinks[EntityLink(true, PARENT_CONNECTION_ID)] = this + } + // else you're attaching a new entity to an existing entity that is not modifiable + _diff.addEntity(value as ModifiableWorkspaceEntityBase) + } + if (_diff != null && (value !is ModifiableWorkspaceEntityBase<*, *> || value.diff != null)) { + _diff.updateOneToOneParentOfChild(PARENT_CONNECTION_ID, this, value) + } + else { + if (value is ModifiableWorkspaceEntityBase<*, *>) { + value.entityLinks[EntityLink(true, PARENT_CONNECTION_ID)] = this + } + // else you're attaching a new entity to an existing entity that is not modifiable + + this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)] = value + } + changedProperty.add("parent") + } + + override fun getEntityClass(): Class = ChildEntityType2::class.java + } +} + +@OptIn(WorkspaceEntityInternalApi::class) +internal class ChildEntityType2Data : WorkspaceEntityData() { + var version: Int = 0 + + + override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntity.Builder { + val modifiable = ChildEntityType2Impl.Builder(null) + modifiable.diff = diff + modifiable.id = createEntityId() + return modifiable + } + + @OptIn(EntityStorageInstrumentationApi::class) + override fun createEntity(snapshot: EntityStorageInstrumentation): ChildEntityType2 { + val entityId = createEntityId() + return snapshot.initializeEntity(entityId) { + val entity = ChildEntityType2Impl(this) + entity.snapshot = snapshot + entity.id = entityId + entity + } + } + + override fun getMetadata(): EntityMetadata { + return MetadataStorageImpl.getMetadataByTypeFqn("com.intellij.workspaceModel.test.api.ChildEntityType2") as EntityMetadata + } + + override fun getEntityInterface(): Class { + return ChildEntityType2::class.java + } + + override fun createDetachedEntity(parents: List>): WorkspaceEntity.Builder<*> { + return ChildEntityType2(version, entitySource) { + parents.filterIsInstance().singleOrNull()?.let { this.parent = it } + } + } + + override fun getRequiredParents(): List> { + val res = mutableListOf>() + res.add(EntityWithChildren::class.java) + return res + } + + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as ChildEntityType2Data + + if (this.entitySource != other.entitySource) return false + if (this.version != other.version) return false + return true + } + + override fun equalsIgnoringEntitySource(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as ChildEntityType2Data + + if (this.version != other.version) return false + return true + } + + override fun hashCode(): Int { + var result = entitySource.hashCode() + result = 31 * result + version.hashCode() + return result + } + + override fun hashCodeIgnoringEntitySource(): Int { + var result = javaClass.hashCode() + result = 31 * result + version.hashCode() + return result + } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/EntityWithChildrenImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/EntityWithChildrenImpl.kt new file mode 100644 index 000000000000..cc5cc4e7e6f8 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/EntityWithChildrenImpl.kt @@ -0,0 +1,286 @@ +package com.intellij.workspaceModel.test.api.impl + +import com.intellij.platform.workspace.storage.ConnectionId +import com.intellij.platform.workspace.storage.EntitySource +import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion +import com.intellij.platform.workspace.storage.GeneratedCodeImplVersion +import com.intellij.platform.workspace.storage.MutableEntityStorage +import com.intellij.platform.workspace.storage.WorkspaceEntity +import com.intellij.platform.workspace.storage.WorkspaceEntityInternalApi +import com.intellij.platform.workspace.storage.annotations.Child +import com.intellij.platform.workspace.storage.impl.EntityLink +import com.intellij.platform.workspace.storage.impl.ModifiableWorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityData +import com.intellij.platform.workspace.storage.impl.extractOneToOneChild +import com.intellij.platform.workspace.storage.impl.updateOneToOneChildOfParent +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentation +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentationApi +import com.intellij.platform.workspace.storage.instrumentation.MutableEntityStorageInstrumentation +import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata +import com.intellij.workspaceModel.test.api.ChildEntityType1 +import com.intellij.workspaceModel.test.api.ChildEntityType2 +import com.intellij.workspaceModel.test.api.EntityWithChildren + +@GeneratedCodeApiVersion(3) +@GeneratedCodeImplVersion(6) +@OptIn(WorkspaceEntityInternalApi::class) +internal class EntityWithChildrenImpl(private val dataSource: EntityWithChildrenData) : EntityWithChildren, + WorkspaceEntityBase(dataSource) { + + private companion object { + internal val PROPERTYCHILD_CONNECTION_ID: ConnectionId = + ConnectionId.create(EntityWithChildren::class.java, ChildEntityType1::class.java, ConnectionId.ConnectionType.ONE_TO_ONE, false) + internal val TYPECHILD_CONNECTION_ID: ConnectionId = + ConnectionId.create(EntityWithChildren::class.java, ChildEntityType2::class.java, ConnectionId.ConnectionType.ONE_TO_ONE, false) + + private val connections = listOf( + PROPERTYCHILD_CONNECTION_ID, + TYPECHILD_CONNECTION_ID, + ) + + } + + override val name: String + get() { + readField("name") + return dataSource.name + } + + override val propertyChild: ChildEntityType1? + get() = snapshot.extractOneToOneChild(PROPERTYCHILD_CONNECTION_ID, this) + + override val typeChild: ChildEntityType2? + get() = snapshot.extractOneToOneChild(TYPECHILD_CONNECTION_ID, this) + + override val entitySource: EntitySource + get() { + readField("entitySource") + return dataSource.entitySource + } + + override fun connectionIdList(): List { + return connections + } + + + internal class Builder(result: EntityWithChildrenData?) : + ModifiableWorkspaceEntityBase(result), EntityWithChildren.Builder { + internal constructor() : this(EntityWithChildrenData()) + + override fun applyToBuilder(builder: MutableEntityStorage) { + if (this.diff != null) { + if (existsInBuilder(builder)) { + this.diff = builder + return + } + else { + error("Entity EntityWithChildren is already created in a different builder") + } + } + + this.diff = builder + addToBuilder() + this.id = getEntityData().createEntityId() + // After adding entity data to the builder, we need to unbind it and move the control over entity data to builder + // Builder may switch to snapshot at any moment and lock entity data to modification + this.currentEntityData = null + + // Process linked entities that are connected without a builder + processLinkedEntities(builder) + checkInitialization() // TODO uncomment and check failed tests + } + + private fun checkInitialization() { + val _diff = diff + if (!getEntityData().isEntitySourceInitialized()) { + error("Field WorkspaceEntity#entitySource should be initialized") + } + if (!getEntityData().isNameInitialized()) { + error("Field EntityWithChildren#name should be initialized") + } + } + + override fun connectionIdList(): List { + return connections + } + + // Relabeling code, move information from dataSource to this builder + override fun relabel(dataSource: WorkspaceEntity, parents: Set?) { + dataSource as EntityWithChildren + if (this.entitySource != dataSource.entitySource) this.entitySource = dataSource.entitySource + if (this.name != dataSource.name) this.name = dataSource.name + updateChildToParentReferences(parents) + } + + + override var entitySource: EntitySource + get() = getEntityData().entitySource + set(value) { + checkModificationAllowed() + getEntityData(true).entitySource = value + changedProperty.add("entitySource") + + } + + override var name: String + get() = getEntityData().name + set(value) { + checkModificationAllowed() + getEntityData(true).name = value + changedProperty.add("name") + } + + override var propertyChild: ChildEntityType1.Builder? + get() { + val _diff = diff + return if (_diff != null) { + @OptIn(EntityStorageInstrumentationApi::class) + ((_diff as MutableEntityStorageInstrumentation).getOneChildBuilder( + PROPERTYCHILD_CONNECTION_ID, this + ) as? ChildEntityType1.Builder) + ?: (this.entityLinks[EntityLink(true, PROPERTYCHILD_CONNECTION_ID)] as? ChildEntityType1.Builder) + } + else { + this.entityLinks[EntityLink(true, PROPERTYCHILD_CONNECTION_ID)] as? ChildEntityType1.Builder + } + } + set(value) { + checkModificationAllowed() + val _diff = diff + if (_diff != null && value is ModifiableWorkspaceEntityBase<*, *> && value.diff == null) { + if (value is ModifiableWorkspaceEntityBase<*, *>) { + value.entityLinks[EntityLink(false, PROPERTYCHILD_CONNECTION_ID)] = this + } + // else you're attaching a new entity to an existing entity that is not modifiable + _diff.addEntity(value as ModifiableWorkspaceEntityBase) + } + if (_diff != null && (value !is ModifiableWorkspaceEntityBase<*, *> || value.diff != null)) { + _diff.updateOneToOneChildOfParent(PROPERTYCHILD_CONNECTION_ID, this, value) + } + else { + if (value is ModifiableWorkspaceEntityBase<*, *>) { + value.entityLinks[EntityLink(false, PROPERTYCHILD_CONNECTION_ID)] = this + } + // else you're attaching a new entity to an existing entity that is not modifiable + + this.entityLinks[EntityLink(true, PROPERTYCHILD_CONNECTION_ID)] = value + } + changedProperty.add("propertyChild") + } + + override var typeChild: ChildEntityType2.Builder? + get() { + val _diff = diff + return if (_diff != null) { + @OptIn(EntityStorageInstrumentationApi::class) + ((_diff as MutableEntityStorageInstrumentation).getOneChildBuilder(TYPECHILD_CONNECTION_ID, this) as? ChildEntityType2.Builder) + ?: (this.entityLinks[EntityLink(true, TYPECHILD_CONNECTION_ID)] as? ChildEntityType2.Builder) + } + else { + this.entityLinks[EntityLink(true, TYPECHILD_CONNECTION_ID)] as? ChildEntityType2.Builder + } + } + set(value) { + checkModificationAllowed() + val _diff = diff + if (_diff != null && value is ModifiableWorkspaceEntityBase<*, *> && value.diff == null) { + if (value is ModifiableWorkspaceEntityBase<*, *>) { + value.entityLinks[EntityLink(false, TYPECHILD_CONNECTION_ID)] = this + } + // else you're attaching a new entity to an existing entity that is not modifiable + _diff.addEntity(value as ModifiableWorkspaceEntityBase) + } + if (_diff != null && (value !is ModifiableWorkspaceEntityBase<*, *> || value.diff != null)) { + _diff.updateOneToOneChildOfParent(TYPECHILD_CONNECTION_ID, this, value) + } + else { + if (value is ModifiableWorkspaceEntityBase<*, *>) { + value.entityLinks[EntityLink(false, TYPECHILD_CONNECTION_ID)] = this + } + // else you're attaching a new entity to an existing entity that is not modifiable + + this.entityLinks[EntityLink(true, TYPECHILD_CONNECTION_ID)] = value + } + changedProperty.add("typeChild") + } + + override fun getEntityClass(): Class = EntityWithChildren::class.java + } +} + +@OptIn(WorkspaceEntityInternalApi::class) +internal class EntityWithChildrenData : WorkspaceEntityData() { + lateinit var name: String + + internal fun isNameInitialized(): Boolean = ::name.isInitialized + + override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntity.Builder { + val modifiable = EntityWithChildrenImpl.Builder(null) + modifiable.diff = diff + modifiable.id = createEntityId() + return modifiable + } + + @OptIn(EntityStorageInstrumentationApi::class) + override fun createEntity(snapshot: EntityStorageInstrumentation): EntityWithChildren { + val entityId = createEntityId() + return snapshot.initializeEntity(entityId) { + val entity = EntityWithChildrenImpl(this) + entity.snapshot = snapshot + entity.id = entityId + entity + } + } + + override fun getMetadata(): EntityMetadata { + return MetadataStorageImpl.getMetadataByTypeFqn("com.intellij.workspaceModel.test.api.EntityWithChildren") as EntityMetadata + } + + override fun getEntityInterface(): Class { + return EntityWithChildren::class.java + } + + override fun createDetachedEntity(parents: List>): WorkspaceEntity.Builder<*> { + return EntityWithChildren(name, entitySource) { + } + } + + override fun getRequiredParents(): List> { + val res = mutableListOf>() + return res + } + + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as EntityWithChildrenData + + if (this.entitySource != other.entitySource) return false + if (this.name != other.name) return false + return true + } + + override fun equalsIgnoringEntitySource(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as EntityWithChildrenData + + if (this.name != other.name) return false + return true + } + + override fun hashCode(): Int { + var result = entitySource.hashCode() + result = 31 * result + name.hashCode() + return result + } + + override fun hashCodeIgnoringEntitySource(): Int { + var result = javaClass.hashCode() + result = 31 * result + name.hashCode() + return result + } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/MetadataStorageImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/MetadataStorageImpl.kt new file mode 100644 index 000000000000..aa6787d78068 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/after/gen/impl/MetadataStorageImpl.kt @@ -0,0 +1,46 @@ +package com.intellij.workspaceModel.test.api.impl + +import com.intellij.platform.workspace.storage.ConnectionId +import com.intellij.platform.workspace.storage.WorkspaceEntityInternalApi +import com.intellij.platform.workspace.storage.metadata.impl.MetadataStorageBase +import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata +import com.intellij.platform.workspace.storage.metadata.model.FinalClassMetadata +import com.intellij.platform.workspace.storage.metadata.model.OwnPropertyMetadata +import com.intellij.platform.workspace.storage.metadata.model.StorageTypeMetadata +import com.intellij.platform.workspace.storage.metadata.model.ValueTypeMetadata + +@OptIn(WorkspaceEntityInternalApi::class) +internal object MetadataStorageImpl: MetadataStorageBase() { + override fun initializeMetadata() { + val primitiveTypeIntNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "Int") + val primitiveTypeStringNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "String") + + var typeMetadata: StorageTypeMetadata + + typeMetadata = EntityMetadata(fqName = "com.intellij.workspaceModel.test.api.ChildEntityType1", entityDataFqName = "com.intellij.workspaceModel.test.api.impl.ChildEntityType1Data", supertypes = listOf("com.intellij.platform.workspace.storage.WorkspaceEntity"), properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "entitySource", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntitySource")), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "version", valueType = primitiveTypeIntNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "parent", valueType = ValueTypeMetadata.EntityReference(connectionType = ConnectionId.ConnectionType.ONE_TO_ONE, entityFqName = "com.intellij.workspaceModel.test.api.EntityWithChildren", isChild = false, isNullable = false), withDefault = false)), extProperties = listOf(), isAbstract = false) + + addMetadata(typeMetadata) + + typeMetadata = EntityMetadata(fqName = "com.intellij.workspaceModel.test.api.ChildEntityType2", entityDataFqName = "com.intellij.workspaceModel.test.api.impl.ChildEntityType2Data", supertypes = listOf("com.intellij.platform.workspace.storage.WorkspaceEntity"), properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "entitySource", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntitySource")), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "version", valueType = primitiveTypeIntNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "parent", valueType = ValueTypeMetadata.EntityReference(connectionType = ConnectionId.ConnectionType.ONE_TO_ONE, entityFqName = "com.intellij.workspaceModel.test.api.EntityWithChildren", isChild = false, isNullable = false), withDefault = false)), extProperties = listOf(), isAbstract = false) + + addMetadata(typeMetadata) + + typeMetadata = EntityMetadata(fqName = "com.intellij.workspaceModel.test.api.EntityWithChildren", entityDataFqName = "com.intellij.workspaceModel.test.api.impl.EntityWithChildrenData", supertypes = listOf("com.intellij.platform.workspace.storage.WorkspaceEntity"), properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "entitySource", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntitySource")), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "propertyChild", valueType = ValueTypeMetadata.EntityReference(connectionType = ConnectionId.ConnectionType.ONE_TO_ONE, entityFqName = "com.intellij.workspaceModel.test.api.ChildEntityType1", isChild = true, isNullable = true), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "typeChild", valueType = ValueTypeMetadata.EntityReference(connectionType = ConnectionId.ConnectionType.ONE_TO_ONE, entityFqName = "com.intellij.workspaceModel.test.api.ChildEntityType2", isChild = true, isNullable = true), withDefault = false)), extProperties = listOf(), isAbstract = false) + + addMetadata(typeMetadata) + } + + override fun initializeMetadataHash() { + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.ChildEntityType1", metadataHash = 1430777503) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.ChildEntityType2", metadataHash = -1568141121) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.EntityWithChildren", metadataHash = 104242451) + } + +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/before/entity.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/before/entity.kt new file mode 100644 index 000000000000..37e028a05bb1 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/entityWithDifferentChildrenTargets/before/entity.kt @@ -0,0 +1,20 @@ +package com.intellij.workspaceModel.test.api + +import com.intellij.platform.workspace.storage.WorkspaceEntity +import com.intellij.platform.workspace.storage.annotations.Child + +interface EntityWithChildren : WorkspaceEntity { + val name: String + val propertyChild: @Child ChildEntityType1? + @Child val typeChild: ChildEntityType2? +} + +interface ChildEntityType1 : WorkspaceEntity { + val version: Int + val parent: EntityWithChildren +} + +interface ChildEntityType2 : WorkspaceEntity { + val version: Int + val parent: EntityWithChildren +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/finalProperty/after/entity.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/finalProperty/after/entity.kt index 0f19dd4826b8..2b0dff4dd05f 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/finalProperty/after/entity.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/finalProperty/after/entity.kt @@ -1,12 +1,12 @@ package com.intellij.workspaceModel.test.api -import com.intellij.platform.workspace.storage.EntityType -import com.intellij.platform.workspace.storage.annotations.Default -import com.intellij.platform.workspace.storage.WorkspaceEntity import com.intellij.platform.workspace.storage.EntitySource +import com.intellij.platform.workspace.storage.EntityType import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion import com.intellij.platform.workspace.storage.ModifiableWorkspaceEntity import com.intellij.platform.workspace.storage.MutableEntityStorage +import com.intellij.platform.workspace.storage.WorkspaceEntity +import com.intellij.platform.workspace.storage.annotations.Default interface FinalFieldsEntity : WorkspaceEntity { diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/finalProperty/after/gen/impl/MetadataStorageImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/finalProperty/after/gen/impl/MetadataStorageImpl.kt index 123479a97d61..d84b5777ea8a 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/finalProperty/after/gen/impl/MetadataStorageImpl.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/finalProperty/after/gen/impl/MetadataStorageImpl.kt @@ -11,27 +11,27 @@ import com.intellij.platform.workspace.storage.metadata.model.ValueTypeMetadata @OptIn(WorkspaceEntityInternalApi::class) internal object MetadataStorageImpl: MetadataStorageBase() { override fun initializeMetadata() { - val primitiveTypeStringNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "String") - val primitiveTypeIntNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "Int") - val primitiveTypeBooleanNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "Boolean") val primitiveTypeStringNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = true, type = "String") + val primitiveTypeStringNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "String") + val primitiveTypeBooleanNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "Boolean") + val primitiveTypeIntNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "Int") var typeMetadata: StorageTypeMetadata typeMetadata = EntityMetadata(fqName = "com.intellij.workspaceModel.test.api.FinalFieldsEntity", entityDataFqName = "com.intellij.workspaceModel.test.api.impl.FinalFieldsEntityData", supertypes = listOf("com.intellij.platform.workspace.storage.WorkspaceEntity"), properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "entitySource", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntitySource")), withDefault = false), -OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "descriptor", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.AnotherDataClass", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), -OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "version", valueType = primitiveTypeIntNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "descriptor", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.AnotherDataClass", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "displayName", valueType = primitiveTypeStringNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "revision", valueType = primitiveTypeStringNullable, withDefault = false), OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "source", valueType = primitiveTypeBooleanNotNullable, withDefault = false), -OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "displayName", valueType = primitiveTypeStringNullable, withDefault = false), OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "url", valueType = primitiveTypeStringNullable, withDefault = false), -OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "revision", valueType = primitiveTypeStringNullable, withDefault = false)), supertypes = listOf())), withDefault = false)), extProperties = listOf(), isAbstract = false) +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "version", valueType = primitiveTypeIntNotNullable, withDefault = false)), supertypes = listOf())), withDefault = false)), extProperties = listOf(), isAbstract = false) addMetadata(typeMetadata) } override fun initializeMetadataHash() { - addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.FinalFieldsEntity", metadataHash = 1499543638) - addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.AnotherDataClass", metadataHash = -1492715258) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.FinalFieldsEntity", metadataHash = -241552936) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.AnotherDataClass", metadataHash = 1379646916) } } diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/entity.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/entity.kt new file mode 100644 index 000000000000..969dbf491856 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/entity.kt @@ -0,0 +1,108 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.workspaceModel.test.api + +import com.intellij.platform.workspace.storage.* +import com.intellij.platform.workspace.storage.annotations.Child +import com.intellij.platform.workspace.storage.url.VirtualFileUrl +import java.net.URL + +interface EntityWithManyImports : WorkspaceEntityWithSymbolicId { + val version: Int + val name: String + val files: List<@Child SimpleEntity> + val pointer: EntityPointer + + override val symbolicId: SimpleId + get() = SimpleId(name) + + //region generated code + @GeneratedCodeApiVersion(3) + interface Builder : WorkspaceEntity.Builder { + override var entitySource: EntitySource + var version: Int + var name: String + var files: List + var pointer: EntityPointer + } + + companion object : EntityType() { + @JvmOverloads + @JvmStatic + @JvmName("create") + operator fun invoke( + version: Int, + name: String, + pointer: EntityPointer, + entitySource: EntitySource, + init: (Builder.() -> Unit)? = null, + ): Builder { + val builder = builder() + builder.version = version + builder.name = name + builder.pointer = pointer + builder.entitySource = entitySource + init?.invoke(builder) + return builder + } + } + //endregion +} + +//region generated code +fun MutableEntityStorage.modifyEntityWithManyImports( + entity: EntityWithManyImports, + modification: EntityWithManyImports.Builder.() -> Unit, +): EntityWithManyImports { + return modifyEntity(EntityWithManyImports.Builder::class.java, entity, modification) +} +//endregion + +data class SimpleId(val name: String) : SymbolicEntityId { + override val presentableName: String + get() = name +} + +interface SimpleEntity : WorkspaceEntity { + val url: VirtualFileUrl + val parent: EntityWithManyImports + + //region generated code + @GeneratedCodeApiVersion(3) + interface Builder : WorkspaceEntity.Builder { + override var entitySource: EntitySource + var url: VirtualFileUrl + var parent: EntityWithManyImports.Builder + } + + companion object : EntityType() { + @JvmOverloads + @JvmStatic + @JvmName("create") + operator fun invoke( + url: VirtualFileUrl, + entitySource: EntitySource, + init: (Builder.() -> Unit)? = null, + ): Builder { + val builder = builder() + builder.url = url + builder.entitySource = entitySource + init?.invoke(builder) + return builder + } + } + //endregion +} + +//region generated code +fun MutableEntityStorage.modifySimpleEntity( + entity: SimpleEntity, + modification: SimpleEntity.Builder.() -> Unit, +): SimpleEntity { + return modifyEntity(SimpleEntity.Builder::class.java, entity, modification) +} +//endregion + +data class UnrelatedToEntities(val name: String, val data: EntityPointer) { + fun doSomething(src: EntitySource) { + } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/gen/impl/EntityWithManyImportsImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/gen/impl/EntityWithManyImportsImpl.kt new file mode 100644 index 000000000000..52b330055a09 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/gen/impl/EntityWithManyImportsImpl.kt @@ -0,0 +1,308 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.workspaceModel.test.api.impl + +import com.intellij.platform.workspace.storage.* +import com.intellij.platform.workspace.storage.annotations.Child +import com.intellij.platform.workspace.storage.impl.EntityLink +import com.intellij.platform.workspace.storage.impl.ModifiableWorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityData +import com.intellij.platform.workspace.storage.impl.extractOneToManyChildren +import com.intellij.platform.workspace.storage.impl.updateOneToManyChildrenOfParent +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentation +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentationApi +import com.intellij.platform.workspace.storage.instrumentation.MutableEntityStorageInstrumentation +import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata +import com.intellij.platform.workspace.storage.url.VirtualFileUrl +import com.intellij.workspaceModel.test.api.EntityWithManyImports +import com.intellij.workspaceModel.test.api.SimpleEntity +import com.intellij.workspaceModel.test.api.SimpleId +import java.net.URL + +@GeneratedCodeApiVersion(3) +@GeneratedCodeImplVersion(6) +@OptIn(WorkspaceEntityInternalApi::class) +internal class EntityWithManyImportsImpl(private val dataSource: EntityWithManyImportsData) : EntityWithManyImports, + WorkspaceEntityBase(dataSource) { + + private companion object { + internal val FILES_CONNECTION_ID: ConnectionId = + ConnectionId.create(EntityWithManyImports::class.java, SimpleEntity::class.java, ConnectionId.ConnectionType.ONE_TO_MANY, false) + + private val connections = listOf( + FILES_CONNECTION_ID, + ) + + } + + override val symbolicId: SimpleId = super.symbolicId + + override val version: Int + get() { + readField("version") + return dataSource.version + } + override val name: String + get() { + readField("name") + return dataSource.name + } + + override val files: List + get() = snapshot.extractOneToManyChildren(FILES_CONNECTION_ID, this)!!.toList() + + override val pointer: EntityPointer + get() { + readField("pointer") + return dataSource.pointer + } + + override val entitySource: EntitySource + get() { + readField("entitySource") + return dataSource.entitySource + } + + override fun connectionIdList(): List { + return connections + } + + + internal class Builder(result: EntityWithManyImportsData?) : + ModifiableWorkspaceEntityBase(result), EntityWithManyImports.Builder { + internal constructor() : this(EntityWithManyImportsData()) + + override fun applyToBuilder(builder: MutableEntityStorage) { + if (this.diff != null) { + if (existsInBuilder(builder)) { + this.diff = builder + return + } + else { + error("Entity EntityWithManyImports is already created in a different builder") + } + } + + this.diff = builder + addToBuilder() + this.id = getEntityData().createEntityId() + // After adding entity data to the builder, we need to unbind it and move the control over entity data to builder + // Builder may switch to snapshot at any moment and lock entity data to modification + this.currentEntityData = null + + // Process linked entities that are connected without a builder + processLinkedEntities(builder) + checkInitialization() // TODO uncomment and check failed tests + } + + private fun checkInitialization() { + val _diff = diff + if (!getEntityData().isEntitySourceInitialized()) { + error("Field WorkspaceEntity#entitySource should be initialized") + } + if (!getEntityData().isNameInitialized()) { + error("Field EntityWithManyImports#name should be initialized") + } + // Check initialization for list with ref type + if (_diff != null) { + if (_diff.extractOneToManyChildren(FILES_CONNECTION_ID, this) == null) { + error("Field EntityWithManyImports#files should be initialized") + } + } + else { + if (this.entityLinks[EntityLink(true, FILES_CONNECTION_ID)] == null) { + error("Field EntityWithManyImports#files should be initialized") + } + } + if (!getEntityData().isPointerInitialized()) { + error("Field EntityWithManyImports#pointer should be initialized") + } + } + + override fun connectionIdList(): List { + return connections + } + + // Relabeling code, move information from dataSource to this builder + override fun relabel(dataSource: WorkspaceEntity, parents: Set?) { + dataSource as EntityWithManyImports + if (this.entitySource != dataSource.entitySource) this.entitySource = dataSource.entitySource + if (this.version != dataSource.version) this.version = dataSource.version + if (this.name != dataSource.name) this.name = dataSource.name + if (this.pointer != dataSource.pointer) this.pointer = dataSource.pointer + updateChildToParentReferences(parents) + } + + + override var entitySource: EntitySource + get() = getEntityData().entitySource + set(value) { + checkModificationAllowed() + getEntityData(true).entitySource = value + changedProperty.add("entitySource") + + } + + override var version: Int + get() = getEntityData().version + set(value) { + checkModificationAllowed() + getEntityData(true).version = value + changedProperty.add("version") + } + + override var name: String + get() = getEntityData().name + set(value) { + checkModificationAllowed() + getEntityData(true).name = value + changedProperty.add("name") + } + + // List of non-abstract referenced types + var _files: List? = emptyList() + override var files: List + get() { + // Getter of the list of non-abstract referenced types + val _diff = diff + return if (_diff != null) { + @OptIn(EntityStorageInstrumentationApi::class) + ((_diff as MutableEntityStorageInstrumentation).getManyChildrenBuilders(FILES_CONNECTION_ID, this)!! + .toList() as List) + + (this.entityLinks[EntityLink(true, FILES_CONNECTION_ID)] as? List ?: emptyList()) + } + else { + this.entityLinks[EntityLink(true, FILES_CONNECTION_ID)] as? List ?: emptyList() + } + } + set(value) { + // Setter of the list of non-abstract referenced types + checkModificationAllowed() + val _diff = diff + if (_diff != null) { + for (item_value in value) { + if (item_value is ModifiableWorkspaceEntityBase<*, *> && (item_value as? ModifiableWorkspaceEntityBase<*, *>)?.diff == null) { + // Backref setup before adding to store + if (item_value is ModifiableWorkspaceEntityBase<*, *>) { + item_value.entityLinks[EntityLink(false, FILES_CONNECTION_ID)] = this + } + // else you're attaching a new entity to an existing entity that is not modifiable + + _diff.addEntity(item_value as ModifiableWorkspaceEntityBase) + } + } + _diff.updateOneToManyChildrenOfParent(FILES_CONNECTION_ID, this, value) + } + else { + for (item_value in value) { + if (item_value is ModifiableWorkspaceEntityBase<*, *>) { + item_value.entityLinks[EntityLink(false, FILES_CONNECTION_ID)] = this + } + // else you're attaching a new entity to an existing entity that is not modifiable + } + + this.entityLinks[EntityLink(true, FILES_CONNECTION_ID)] = value + } + changedProperty.add("files") + } + + override var pointer: EntityPointer + get() = getEntityData().pointer + set(value) { + checkModificationAllowed() + getEntityData(true).pointer = value + changedProperty.add("pointer") + + } + + override fun getEntityClass(): Class = EntityWithManyImports::class.java + } +} + +@OptIn(WorkspaceEntityInternalApi::class) +internal class EntityWithManyImportsData : WorkspaceEntityData() { + var version: Int = 0 + lateinit var name: String + lateinit var pointer: EntityPointer + + + internal fun isNameInitialized(): Boolean = ::name.isInitialized + internal fun isPointerInitialized(): Boolean = ::pointer.isInitialized + + override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntity.Builder { + val modifiable = EntityWithManyImportsImpl.Builder(null) + modifiable.diff = diff + modifiable.id = createEntityId() + return modifiable + } + + @OptIn(EntityStorageInstrumentationApi::class) + override fun createEntity(snapshot: EntityStorageInstrumentation): EntityWithManyImports { + val entityId = createEntityId() + return snapshot.initializeEntity(entityId) { + val entity = EntityWithManyImportsImpl(this) + entity.snapshot = snapshot + entity.id = entityId + entity + } + } + + override fun getMetadata(): EntityMetadata { + return MetadataStorageImpl.getMetadataByTypeFqn("com.intellij.workspaceModel.test.api.EntityWithManyImports") as EntityMetadata + } + + override fun getEntityInterface(): Class { + return EntityWithManyImports::class.java + } + + override fun createDetachedEntity(parents: List>): WorkspaceEntity.Builder<*> { + return EntityWithManyImports(version, name, pointer, entitySource) { + } + } + + override fun getRequiredParents(): List> { + val res = mutableListOf>() + return res + } + + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as EntityWithManyImportsData + + if (this.entitySource != other.entitySource) return false + if (this.version != other.version) return false + if (this.name != other.name) return false + if (this.pointer != other.pointer) return false + return true + } + + override fun equalsIgnoringEntitySource(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as EntityWithManyImportsData + + if (this.version != other.version) return false + if (this.name != other.name) return false + if (this.pointer != other.pointer) return false + return true + } + + override fun hashCode(): Int { + var result = entitySource.hashCode() + result = 31 * result + version.hashCode() + result = 31 * result + name.hashCode() + result = 31 * result + pointer.hashCode() + return result + } + + override fun hashCodeIgnoringEntitySource(): Int { + var result = javaClass.hashCode() + result = 31 * result + version.hashCode() + result = 31 * result + name.hashCode() + result = 31 * result + pointer.hashCode() + return result + } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/gen/impl/MetadataStorageImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/gen/impl/MetadataStorageImpl.kt new file mode 100644 index 000000000000..ebb1af146cd8 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/gen/impl/MetadataStorageImpl.kt @@ -0,0 +1,50 @@ +package com.intellij.workspaceModel.test.api.impl + +import com.intellij.platform.workspace.storage.ConnectionId +import com.intellij.platform.workspace.storage.WorkspaceEntityInternalApi +import com.intellij.platform.workspace.storage.metadata.impl.MetadataStorageBase +import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata +import com.intellij.platform.workspace.storage.metadata.model.FinalClassMetadata +import com.intellij.platform.workspace.storage.metadata.model.OwnPropertyMetadata +import com.intellij.platform.workspace.storage.metadata.model.StorageTypeMetadata +import com.intellij.platform.workspace.storage.metadata.model.ValueTypeMetadata + +@OptIn(WorkspaceEntityInternalApi::class) +internal object MetadataStorageImpl: MetadataStorageBase() { + override fun initializeMetadata() { + val primitiveTypeStringNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "String") + val primitiveTypeIntNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "Int") + + var typeMetadata: StorageTypeMetadata + + typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "presentableName", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf("com.intellij.platform.workspace.storage.SymbolicEntityId")) + + addMetadata(typeMetadata) + + typeMetadata = EntityMetadata(fqName = "com.intellij.workspaceModel.test.api.EntityWithManyImports", entityDataFqName = "com.intellij.workspaceModel.test.api.impl.EntityWithManyImportsData", supertypes = listOf("com.intellij.platform.workspace.storage.WorkspaceEntity", +"com.intellij.platform.workspace.storage.WorkspaceEntityWithSymbolicId"), properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "entitySource", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntitySource")), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "version", valueType = primitiveTypeIntNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "files", valueType = ValueTypeMetadata.EntityReference(connectionType = ConnectionId.ConnectionType.ONE_TO_MANY, entityFqName = "com.intellij.workspaceModel.test.api.SimpleEntity", isChild = true, isNullable = false), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "pointer", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntityPointer")), withDefault = false), +OwnPropertyMetadata(isComputable = true, isKey = false, isOpen = false, name = "symbolicId", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "presentableName", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf("com.intellij.platform.workspace.storage.SymbolicEntityId"))), withDefault = false)), extProperties = listOf(), isAbstract = false) + + addMetadata(typeMetadata) + + typeMetadata = EntityMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleEntity", entityDataFqName = "com.intellij.workspaceModel.test.api.impl.SimpleEntityData", supertypes = listOf("com.intellij.platform.workspace.storage.WorkspaceEntity"), properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "entitySource", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntitySource")), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "url", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.url.VirtualFileUrl")), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "parent", valueType = ValueTypeMetadata.EntityReference(connectionType = ConnectionId.ConnectionType.ONE_TO_MANY, entityFqName = "com.intellij.workspaceModel.test.api.EntityWithManyImports", isChild = false, isNullable = false), withDefault = false)), extProperties = listOf(), isAbstract = false) + + addMetadata(typeMetadata) + } + + override fun initializeMetadataHash() { + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.EntityWithManyImports", metadataHash = 279598191) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SimpleEntity", metadataHash = 8198385) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SimpleId", metadataHash = 1096965603) + addMetadataHash(typeFqn = "com.intellij.platform.workspace.storage.SymbolicEntityId", metadataHash = -1044278955) + } + +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/gen/impl/SimpleEntityImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/gen/impl/SimpleEntityImpl.kt new file mode 100644 index 000000000000..0887d4ff231b --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/after/gen/impl/SimpleEntityImpl.kt @@ -0,0 +1,256 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.workspaceModel.test.api.impl + +import com.intellij.platform.workspace.storage.* +import com.intellij.platform.workspace.storage.annotations.Child +import com.intellij.platform.workspace.storage.impl.EntityLink +import com.intellij.platform.workspace.storage.impl.ModifiableWorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityData +import com.intellij.platform.workspace.storage.impl.extractOneToManyParent +import com.intellij.platform.workspace.storage.impl.updateOneToManyParentOfChild +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentation +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentationApi +import com.intellij.platform.workspace.storage.instrumentation.MutableEntityStorageInstrumentation +import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata +import com.intellij.platform.workspace.storage.url.VirtualFileUrl +import com.intellij.workspaceModel.test.api.EntityWithManyImports +import com.intellij.workspaceModel.test.api.SimpleEntity +import java.net.URL + +@GeneratedCodeApiVersion(3) +@GeneratedCodeImplVersion(6) +@OptIn(WorkspaceEntityInternalApi::class) +internal class SimpleEntityImpl(private val dataSource: SimpleEntityData) : SimpleEntity, WorkspaceEntityBase(dataSource) { + + private companion object { + internal val PARENT_CONNECTION_ID: ConnectionId = + ConnectionId.create(EntityWithManyImports::class.java, SimpleEntity::class.java, ConnectionId.ConnectionType.ONE_TO_MANY, false) + + private val connections = listOf( + PARENT_CONNECTION_ID, + ) + + } + + override val url: VirtualFileUrl + get() { + readField("url") + return dataSource.url + } + + override val parent: EntityWithManyImports + get() = snapshot.extractOneToManyParent(PARENT_CONNECTION_ID, this)!! + + override val entitySource: EntitySource + get() { + readField("entitySource") + return dataSource.entitySource + } + + override fun connectionIdList(): List { + return connections + } + + + internal class Builder(result: SimpleEntityData?) : ModifiableWorkspaceEntityBase(result), + SimpleEntity.Builder { + internal constructor() : this(SimpleEntityData()) + + override fun applyToBuilder(builder: MutableEntityStorage) { + if (this.diff != null) { + if (existsInBuilder(builder)) { + this.diff = builder + return + } + else { + error("Entity SimpleEntity is already created in a different builder") + } + } + + this.diff = builder + addToBuilder() + this.id = getEntityData().createEntityId() + // After adding entity data to the builder, we need to unbind it and move the control over entity data to builder + // Builder may switch to snapshot at any moment and lock entity data to modification + this.currentEntityData = null + + index(this, "url", this.url) + // Process linked entities that are connected without a builder + processLinkedEntities(builder) + checkInitialization() // TODO uncomment and check failed tests + } + + private fun checkInitialization() { + val _diff = diff + if (!getEntityData().isEntitySourceInitialized()) { + error("Field WorkspaceEntity#entitySource should be initialized") + } + if (!getEntityData().isUrlInitialized()) { + error("Field SimpleEntity#url should be initialized") + } + if (_diff != null) { + if (_diff.extractOneToManyParent(PARENT_CONNECTION_ID, this) == null) { + error("Field SimpleEntity#parent should be initialized") + } + } + else { + if (this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)] == null) { + error("Field SimpleEntity#parent should be initialized") + } + } + } + + override fun connectionIdList(): List { + return connections + } + + // Relabeling code, move information from dataSource to this builder + override fun relabel(dataSource: WorkspaceEntity, parents: Set?) { + dataSource as SimpleEntity + if (this.entitySource != dataSource.entitySource) this.entitySource = dataSource.entitySource + if (this.url != dataSource.url) this.url = dataSource.url + updateChildToParentReferences(parents) + } + + + override var entitySource: EntitySource + get() = getEntityData().entitySource + set(value) { + checkModificationAllowed() + getEntityData(true).entitySource = value + changedProperty.add("entitySource") + + } + + override var url: VirtualFileUrl + get() = getEntityData().url + set(value) { + checkModificationAllowed() + getEntityData(true).url = value + changedProperty.add("url") + val _diff = diff + if (_diff != null) index(this, "url", value) + } + + override var parent: EntityWithManyImports.Builder + get() { + val _diff = diff + return if (_diff != null) { + @OptIn(EntityStorageInstrumentationApi::class) + ((_diff as MutableEntityStorageInstrumentation).getParentBuilder(PARENT_CONNECTION_ID, this) as? EntityWithManyImports.Builder) + ?: (this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)]!! as EntityWithManyImports.Builder) + } + else { + this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)]!! as EntityWithManyImports.Builder + } + } + set(value) { + checkModificationAllowed() + val _diff = diff + if (_diff != null && value is ModifiableWorkspaceEntityBase<*, *> && value.diff == null) { + // Setting backref of the list + if (value is ModifiableWorkspaceEntityBase<*, *>) { + val data = (value.entityLinks[EntityLink(true, PARENT_CONNECTION_ID)] as? List ?: emptyList()) + this + value.entityLinks[EntityLink(true, PARENT_CONNECTION_ID)] = data + } + // else you're attaching a new entity to an existing entity that is not modifiable + _diff.addEntity(value as ModifiableWorkspaceEntityBase) + } + if (_diff != null && (value !is ModifiableWorkspaceEntityBase<*, *> || value.diff != null)) { + _diff.updateOneToManyParentOfChild(PARENT_CONNECTION_ID, this, value) + } + else { + // Setting backref of the list + if (value is ModifiableWorkspaceEntityBase<*, *>) { + val data = (value.entityLinks[EntityLink(true, PARENT_CONNECTION_ID)] as? List ?: emptyList()) + this + value.entityLinks[EntityLink(true, PARENT_CONNECTION_ID)] = data + } + // else you're attaching a new entity to an existing entity that is not modifiable + + this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)] = value + } + changedProperty.add("parent") + } + + override fun getEntityClass(): Class = SimpleEntity::class.java + } +} + +@OptIn(WorkspaceEntityInternalApi::class) +internal class SimpleEntityData : WorkspaceEntityData() { + lateinit var url: VirtualFileUrl + + internal fun isUrlInitialized(): Boolean = ::url.isInitialized + + override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntity.Builder { + val modifiable = SimpleEntityImpl.Builder(null) + modifiable.diff = diff + modifiable.id = createEntityId() + return modifiable + } + + @OptIn(EntityStorageInstrumentationApi::class) + override fun createEntity(snapshot: EntityStorageInstrumentation): SimpleEntity { + val entityId = createEntityId() + return snapshot.initializeEntity(entityId) { + val entity = SimpleEntityImpl(this) + entity.snapshot = snapshot + entity.id = entityId + entity + } + } + + override fun getMetadata(): EntityMetadata { + return MetadataStorageImpl.getMetadataByTypeFqn("com.intellij.workspaceModel.test.api.SimpleEntity") as EntityMetadata + } + + override fun getEntityInterface(): Class { + return SimpleEntity::class.java + } + + override fun createDetachedEntity(parents: List>): WorkspaceEntity.Builder<*> { + return SimpleEntity(url, entitySource) { + parents.filterIsInstance().singleOrNull()?.let { this.parent = it } + } + } + + override fun getRequiredParents(): List> { + val res = mutableListOf>() + res.add(EntityWithManyImports::class.java) + return res + } + + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as SimpleEntityData + + if (this.entitySource != other.entitySource) return false + if (this.url != other.url) return false + return true + } + + override fun equalsIgnoringEntitySource(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as SimpleEntityData + + if (this.url != other.url) return false + return true + } + + override fun hashCode(): Int { + var result = entitySource.hashCode() + result = 31 * result + url.hashCode() + return result + } + + override fun hashCodeIgnoringEntitySource(): Int { + var result = javaClass.hashCode() + result = 31 * result + url.hashCode() + return result + } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/before/entity.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/before/entity.kt new file mode 100644 index 000000000000..2a1e4686c653 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/imports/before/entity.kt @@ -0,0 +1,33 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.workspaceModel.test.api + +import com.intellij.platform.workspace.storage.* +import com.intellij.platform.workspace.storage.WorkspaceEntityWithSymbolicId // stoopid and unnecessary +import java.net.URL // stoopid but necessary (user-added) +import com.intellij.platform.workspace.storage.annotations.Child +import com.intellij.platform.workspace.storage.url.VirtualFileUrl + +interface EntityWithManyImports : WorkspaceEntityWithSymbolicId { + val version: Int + val name: String + val files: List<@Child SimpleEntity> + val pointer: EntityPointer + + override val symbolicId: SimpleId + get() = SimpleId(name) +} + +data class SimpleId(val name: String) : SymbolicEntityId { + override val presentableName: String + get() = name +} + +interface SimpleEntity : WorkspaceEntity { + val url: VirtualFileUrl + val parent: EntityWithManyImports +} + +data class UnrelatedToEntities(val name: String, val data: EntityPointer) { + fun doSomething(src: EntitySource) { + } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/entity.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/entity.kt new file mode 100644 index 000000000000..8c23782a4453 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/entity.kt @@ -0,0 +1,181 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.workspaceModel.test.api + +import com.intellij.platform.workspace.storage.* +import com.intellij.platform.workspace.storage.annotations.Abstract + +interface SimpleEntity : WorkspaceEntityWithSymbolicId { + override val symbolicId: SimpleId + get() = SimpleId(name) + + val name: String + + //region generated code + @GeneratedCodeApiVersion(3) + interface Builder : WorkspaceEntity.Builder { + override var entitySource: EntitySource + var name: String + } + + companion object : EntityType() { + @JvmOverloads + @JvmStatic + @JvmName("create") + operator fun invoke( + name: String, + entitySource: EntitySource, + init: (Builder.() -> Unit)? = null, + ): Builder { + val builder = builder() + builder.name = name + builder.entitySource = entitySource + init?.invoke(builder) + return builder + } + } + //endregion +} + +//region generated code +fun MutableEntityStorage.modifySimpleEntity( + entity: SimpleEntity, + modification: SimpleEntity.Builder.() -> Unit, +): SimpleEntity { + return modifyEntity(SimpleEntity.Builder::class.java, entity, modification) +} +//endregion + +data class SimpleId(val name: String) : SymbolicEntityId { + override val presentableName: String + get() = name +} + +// partial copy of org.jetbrains.kotlin.idea.workspaceModel.KotlinSettingsEntity +@Abstract +interface BaseEntity : WorkspaceEntity { + val name: String + val moduleId: SimpleId + + val aBaseEntityProperty: String + val dBaseEntityProperty: String + val bBaseEntityProperty: String + + val sealedDataClassProperty: BaseDataClass + + //region generated code + @GeneratedCodeApiVersion(3) + interface Builder : WorkspaceEntity.Builder { + override var entitySource: EntitySource + var name: String + var moduleId: SimpleId + var aBaseEntityProperty: String + var dBaseEntityProperty: String + var bBaseEntityProperty: String + var sealedDataClassProperty: BaseDataClass + } + + companion object : EntityType>() { + @JvmOverloads + @JvmStatic + @JvmName("create") + operator fun invoke( + name: String, + moduleId: SimpleId, + aBaseEntityProperty: String, + dBaseEntityProperty: String, + bBaseEntityProperty: String, + sealedDataClassProperty: BaseDataClass, + entitySource: EntitySource, + init: (Builder.() -> Unit)? = null, + ): Builder { + val builder = builder() + builder.name = name + builder.moduleId = moduleId + builder.aBaseEntityProperty = aBaseEntityProperty + builder.dBaseEntityProperty = dBaseEntityProperty + builder.bBaseEntityProperty = bBaseEntityProperty + builder.sealedDataClassProperty = sealedDataClassProperty + builder.entitySource = entitySource + init?.invoke(builder) + return builder + } + } + //endregion +} + +interface ChildEntity : BaseEntity { + val cChildEntityProperty: String + + //region generated code + @GeneratedCodeApiVersion(3) + interface Builder : WorkspaceEntity.Builder, BaseEntity.Builder { + override var entitySource: EntitySource + override var name: String + override var moduleId: SimpleId + override var aBaseEntityProperty: String + override var dBaseEntityProperty: String + override var bBaseEntityProperty: String + override var sealedDataClassProperty: BaseDataClass + var cChildEntityProperty: String + } + + companion object : EntityType(BaseEntity) { + @JvmOverloads + @JvmStatic + @JvmName("create") + operator fun invoke( + name: String, + moduleId: SimpleId, + aBaseEntityProperty: String, + dBaseEntityProperty: String, + bBaseEntityProperty: String, + sealedDataClassProperty: BaseDataClass, + cChildEntityProperty: String, + entitySource: EntitySource, + init: (Builder.() -> Unit)? = null, + ): Builder { + val builder = builder() + builder.name = name + builder.moduleId = moduleId + builder.aBaseEntityProperty = aBaseEntityProperty + builder.dBaseEntityProperty = dBaseEntityProperty + builder.bBaseEntityProperty = bBaseEntityProperty + builder.sealedDataClassProperty = sealedDataClassProperty + builder.cChildEntityProperty = cChildEntityProperty + builder.entitySource = entitySource + init?.invoke(builder) + return builder + } + } + //endregion +} + +//region generated code +fun MutableEntityStorage.modifyChildEntity( + entity: ChildEntity, + modification: ChildEntity.Builder.() -> Unit, +): ChildEntity { + return modifyEntity(ChildEntity.Builder::class.java, entity, modification) +} +//endregion + +sealed class BaseDataClass(val baseConstructorProperty: String) { + open val baseBodyProperty: Int = 15 + val anotherBaseBodyProperty: Int = 16 +} + +sealed class DerivedDataClass( + baseConstructorPropertyValue: String, + val derivedConstructorProperty: String +) : BaseDataClass(baseConstructorPropertyValue) { + val derivedBodyProperty: Int = 23 +} + +class DerivedDerivedDataClass( + baseConstructorPropertyValue: String, + override val baseBodyProperty: Int, + derivedConstructorPropertyValue: String, + val derivedDerivedConstructorProperty: String +) : DerivedDataClass(baseConstructorPropertyValue, derivedConstructorPropertyValue) { + val deriveDerivedBodyProperty: Int = 42 +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/gen/impl/ChildEntityImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/gen/impl/ChildEntityImpl.kt new file mode 100644 index 000000000000..206a7b0a51e7 --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/gen/impl/ChildEntityImpl.kt @@ -0,0 +1,427 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.workspaceModel.test.api.impl + +import com.intellij.platform.workspace.storage.* +import com.intellij.platform.workspace.storage.annotations.Abstract +import com.intellij.platform.workspace.storage.impl.ModifiableWorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.SoftLinkable +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityData +import com.intellij.platform.workspace.storage.impl.indices.WorkspaceMutableIndex +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentation +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentationApi +import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata +import com.intellij.workspaceModel.test.api.BaseDataClass +import com.intellij.workspaceModel.test.api.ChildEntity +import com.intellij.workspaceModel.test.api.DerivedDataClass +import com.intellij.workspaceModel.test.api.DerivedDerivedDataClass +import com.intellij.workspaceModel.test.api.SimpleId + +@GeneratedCodeApiVersion(3) +@GeneratedCodeImplVersion(6) +@OptIn(WorkspaceEntityInternalApi::class) +internal class ChildEntityImpl(private val dataSource: ChildEntityData) : ChildEntity, WorkspaceEntityBase(dataSource) { + + private companion object { + + + private val connections = listOf( + ) + + } + + override val name: String + get() { + readField("name") + return dataSource.name + } + + override val moduleId: SimpleId + get() { + readField("moduleId") + return dataSource.moduleId + } + + override val aBaseEntityProperty: String + get() { + readField("aBaseEntityProperty") + return dataSource.aBaseEntityProperty + } + + override val dBaseEntityProperty: String + get() { + readField("dBaseEntityProperty") + return dataSource.dBaseEntityProperty + } + + override val bBaseEntityProperty: String + get() { + readField("bBaseEntityProperty") + return dataSource.bBaseEntityProperty + } + + override val sealedDataClassProperty: BaseDataClass + get() { + readField("sealedDataClassProperty") + return dataSource.sealedDataClassProperty + } + + override val cChildEntityProperty: String + get() { + readField("cChildEntityProperty") + return dataSource.cChildEntityProperty + } + + override val entitySource: EntitySource + get() { + readField("entitySource") + return dataSource.entitySource + } + + override fun connectionIdList(): List { + return connections + } + + + internal class Builder(result: ChildEntityData?) : ModifiableWorkspaceEntityBase(result), + ChildEntity.Builder { + internal constructor() : this(ChildEntityData()) + + override fun applyToBuilder(builder: MutableEntityStorage) { + if (this.diff != null) { + if (existsInBuilder(builder)) { + this.diff = builder + return + } + else { + error("Entity ChildEntity is already created in a different builder") + } + } + + this.diff = builder + addToBuilder() + this.id = getEntityData().createEntityId() + // After adding entity data to the builder, we need to unbind it and move the control over entity data to builder + // Builder may switch to snapshot at any moment and lock entity data to modification + this.currentEntityData = null + + // Process linked entities that are connected without a builder + processLinkedEntities(builder) + checkInitialization() // TODO uncomment and check failed tests + } + + private fun checkInitialization() { + val _diff = diff + if (!getEntityData().isEntitySourceInitialized()) { + error("Field WorkspaceEntity#entitySource should be initialized") + } + if (!getEntityData().isNameInitialized()) { + error("Field BaseEntity#name should be initialized") + } + if (!getEntityData().isModuleIdInitialized()) { + error("Field BaseEntity#moduleId should be initialized") + } + if (!getEntityData().isABaseEntityPropertyInitialized()) { + error("Field BaseEntity#aBaseEntityProperty should be initialized") + } + if (!getEntityData().isDBaseEntityPropertyInitialized()) { + error("Field BaseEntity#dBaseEntityProperty should be initialized") + } + if (!getEntityData().isBBaseEntityPropertyInitialized()) { + error("Field BaseEntity#bBaseEntityProperty should be initialized") + } + if (!getEntityData().isSealedDataClassPropertyInitialized()) { + error("Field BaseEntity#sealedDataClassProperty should be initialized") + } + if (!getEntityData().isCChildEntityPropertyInitialized()) { + error("Field ChildEntity#cChildEntityProperty should be initialized") + } + } + + override fun connectionIdList(): List { + return connections + } + + // Relabeling code, move information from dataSource to this builder + override fun relabel(dataSource: WorkspaceEntity, parents: Set?) { + dataSource as ChildEntity + if (this.entitySource != dataSource.entitySource) this.entitySource = dataSource.entitySource + if (this.name != dataSource.name) this.name = dataSource.name + if (this.moduleId != dataSource.moduleId) this.moduleId = dataSource.moduleId + if (this.aBaseEntityProperty != dataSource.aBaseEntityProperty) this.aBaseEntityProperty = dataSource.aBaseEntityProperty + if (this.dBaseEntityProperty != dataSource.dBaseEntityProperty) this.dBaseEntityProperty = dataSource.dBaseEntityProperty + if (this.bBaseEntityProperty != dataSource.bBaseEntityProperty) this.bBaseEntityProperty = dataSource.bBaseEntityProperty + if (this.sealedDataClassProperty != dataSource.sealedDataClassProperty) this.sealedDataClassProperty = + dataSource.sealedDataClassProperty + if (this.cChildEntityProperty != dataSource.cChildEntityProperty) this.cChildEntityProperty = dataSource.cChildEntityProperty + updateChildToParentReferences(parents) + } + + + override var entitySource: EntitySource + get() = getEntityData().entitySource + set(value) { + checkModificationAllowed() + getEntityData(true).entitySource = value + changedProperty.add("entitySource") + + } + + override var name: String + get() = getEntityData().name + set(value) { + checkModificationAllowed() + getEntityData(true).name = value + changedProperty.add("name") + } + + override var moduleId: SimpleId + get() = getEntityData().moduleId + set(value) { + checkModificationAllowed() + getEntityData(true).moduleId = value + changedProperty.add("moduleId") + + } + + override var aBaseEntityProperty: String + get() = getEntityData().aBaseEntityProperty + set(value) { + checkModificationAllowed() + getEntityData(true).aBaseEntityProperty = value + changedProperty.add("aBaseEntityProperty") + } + + override var dBaseEntityProperty: String + get() = getEntityData().dBaseEntityProperty + set(value) { + checkModificationAllowed() + getEntityData(true).dBaseEntityProperty = value + changedProperty.add("dBaseEntityProperty") + } + + override var bBaseEntityProperty: String + get() = getEntityData().bBaseEntityProperty + set(value) { + checkModificationAllowed() + getEntityData(true).bBaseEntityProperty = value + changedProperty.add("bBaseEntityProperty") + } + + override var sealedDataClassProperty: BaseDataClass + get() = getEntityData().sealedDataClassProperty + set(value) { + checkModificationAllowed() + getEntityData(true).sealedDataClassProperty = value + changedProperty.add("sealedDataClassProperty") + + } + + override var cChildEntityProperty: String + get() = getEntityData().cChildEntityProperty + set(value) { + checkModificationAllowed() + getEntityData(true).cChildEntityProperty = value + changedProperty.add("cChildEntityProperty") + } + + override fun getEntityClass(): Class = ChildEntity::class.java + } +} + +@OptIn(WorkspaceEntityInternalApi::class) +internal class ChildEntityData : WorkspaceEntityData(), SoftLinkable { + lateinit var name: String + lateinit var moduleId: SimpleId + lateinit var aBaseEntityProperty: String + lateinit var dBaseEntityProperty: String + lateinit var bBaseEntityProperty: String + lateinit var sealedDataClassProperty: BaseDataClass + lateinit var cChildEntityProperty: String + + internal fun isNameInitialized(): Boolean = ::name.isInitialized + internal fun isModuleIdInitialized(): Boolean = ::moduleId.isInitialized + internal fun isABaseEntityPropertyInitialized(): Boolean = ::aBaseEntityProperty.isInitialized + internal fun isDBaseEntityPropertyInitialized(): Boolean = ::dBaseEntityProperty.isInitialized + internal fun isBBaseEntityPropertyInitialized(): Boolean = ::bBaseEntityProperty.isInitialized + internal fun isSealedDataClassPropertyInitialized(): Boolean = ::sealedDataClassProperty.isInitialized + internal fun isCChildEntityPropertyInitialized(): Boolean = ::cChildEntityProperty.isInitialized + + override fun getLinks(): Set> { + val result = HashSet>() + result.add(moduleId) + val _sealedDataClassProperty = sealedDataClassProperty + when (_sealedDataClassProperty) { + is DerivedDataClass -> { + val __sealedDataClassProperty = _sealedDataClassProperty + when (__sealedDataClassProperty) { + is DerivedDerivedDataClass -> { + } + } + } + } + return result + } + + override fun index(index: WorkspaceMutableIndex>) { + index.index(this, moduleId) + val _sealedDataClassProperty = sealedDataClassProperty + when (_sealedDataClassProperty) { + is DerivedDataClass -> { + val __sealedDataClassProperty = _sealedDataClassProperty + when (__sealedDataClassProperty) { + is DerivedDerivedDataClass -> { + } + } + } + } + } + + override fun updateLinksIndex(prev: Set>, index: WorkspaceMutableIndex>) { + // TODO verify logic + val mutablePreviousSet = HashSet(prev) + val removedItem_moduleId = mutablePreviousSet.remove(moduleId) + if (!removedItem_moduleId) { + index.index(this, moduleId) + } + val _sealedDataClassProperty = sealedDataClassProperty + when (_sealedDataClassProperty) { + is DerivedDataClass -> { + val __sealedDataClassProperty = _sealedDataClassProperty + when (__sealedDataClassProperty) { + is DerivedDerivedDataClass -> { + } + } + } + } + for (removed in mutablePreviousSet) { + index.remove(this, removed) + } + } + + override fun updateLink(oldLink: SymbolicEntityId<*>, newLink: SymbolicEntityId<*>): Boolean { + var changed = false + val moduleId_data = if (moduleId == oldLink) { + changed = true + newLink as SimpleId + } + else { + null + } + if (moduleId_data != null) { + moduleId = moduleId_data + } + val _sealedDataClassProperty = sealedDataClassProperty + val res_sealedDataClassProperty = when (_sealedDataClassProperty) { + is DerivedDataClass -> { + val __sealedDataClassProperty = _sealedDataClassProperty + val res__sealedDataClassProperty = when (__sealedDataClassProperty) { + is DerivedDerivedDataClass -> { + __sealedDataClassProperty + } + } + res__sealedDataClassProperty + } + } + if (res_sealedDataClassProperty != null) { + sealedDataClassProperty = res_sealedDataClassProperty + } + return changed + } + + override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntity.Builder { + val modifiable = ChildEntityImpl.Builder(null) + modifiable.diff = diff + modifiable.id = createEntityId() + return modifiable + } + + @OptIn(EntityStorageInstrumentationApi::class) + override fun createEntity(snapshot: EntityStorageInstrumentation): ChildEntity { + val entityId = createEntityId() + return snapshot.initializeEntity(entityId) { + val entity = ChildEntityImpl(this) + entity.snapshot = snapshot + entity.id = entityId + entity + } + } + + override fun getMetadata(): EntityMetadata { + return MetadataStorageImpl.getMetadataByTypeFqn("com.intellij.workspaceModel.test.api.ChildEntity") as EntityMetadata + } + + override fun getEntityInterface(): Class { + return ChildEntity::class.java + } + + override fun createDetachedEntity(parents: List>): WorkspaceEntity.Builder<*> { + return ChildEntity( + name, moduleId, aBaseEntityProperty, dBaseEntityProperty, bBaseEntityProperty, sealedDataClassProperty, cChildEntityProperty, + entitySource + ) { + } + } + + override fun getRequiredParents(): List> { + val res = mutableListOf>() + return res + } + + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as ChildEntityData + + if (this.entitySource != other.entitySource) return false + if (this.name != other.name) return false + if (this.moduleId != other.moduleId) return false + if (this.aBaseEntityProperty != other.aBaseEntityProperty) return false + if (this.dBaseEntityProperty != other.dBaseEntityProperty) return false + if (this.bBaseEntityProperty != other.bBaseEntityProperty) return false + if (this.sealedDataClassProperty != other.sealedDataClassProperty) return false + if (this.cChildEntityProperty != other.cChildEntityProperty) return false + return true + } + + override fun equalsIgnoringEntitySource(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as ChildEntityData + + if (this.name != other.name) return false + if (this.moduleId != other.moduleId) return false + if (this.aBaseEntityProperty != other.aBaseEntityProperty) return false + if (this.dBaseEntityProperty != other.dBaseEntityProperty) return false + if (this.bBaseEntityProperty != other.bBaseEntityProperty) return false + if (this.sealedDataClassProperty != other.sealedDataClassProperty) return false + if (this.cChildEntityProperty != other.cChildEntityProperty) return false + return true + } + + override fun hashCode(): Int { + var result = entitySource.hashCode() + result = 31 * result + name.hashCode() + result = 31 * result + moduleId.hashCode() + result = 31 * result + aBaseEntityProperty.hashCode() + result = 31 * result + dBaseEntityProperty.hashCode() + result = 31 * result + bBaseEntityProperty.hashCode() + result = 31 * result + sealedDataClassProperty.hashCode() + result = 31 * result + cChildEntityProperty.hashCode() + return result + } + + override fun hashCodeIgnoringEntitySource(): Int { + var result = javaClass.hashCode() + result = 31 * result + name.hashCode() + result = 31 * result + moduleId.hashCode() + result = 31 * result + aBaseEntityProperty.hashCode() + result = 31 * result + dBaseEntityProperty.hashCode() + result = 31 * result + bBaseEntityProperty.hashCode() + result = 31 * result + sealedDataClassProperty.hashCode() + result = 31 * result + cChildEntityProperty.hashCode() + return result + } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/gen/impl/MetadataStorageImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/gen/impl/MetadataStorageImpl.kt new file mode 100644 index 000000000000..18b419fb17cf --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/gen/impl/MetadataStorageImpl.kt @@ -0,0 +1,83 @@ +package com.intellij.workspaceModel.test.api.impl + +import com.intellij.platform.workspace.storage.WorkspaceEntityInternalApi +import com.intellij.platform.workspace.storage.metadata.impl.MetadataStorageBase +import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata +import com.intellij.platform.workspace.storage.metadata.model.ExtendableClassMetadata +import com.intellij.platform.workspace.storage.metadata.model.FinalClassMetadata +import com.intellij.platform.workspace.storage.metadata.model.OwnPropertyMetadata +import com.intellij.platform.workspace.storage.metadata.model.StorageTypeMetadata +import com.intellij.platform.workspace.storage.metadata.model.ValueTypeMetadata + +@OptIn(WorkspaceEntityInternalApi::class) +internal object MetadataStorageImpl: MetadataStorageBase() { + override fun initializeMetadata() { + val primitiveTypeStringNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "String") + val primitiveTypeIntNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "Int") + + var typeMetadata: StorageTypeMetadata + + typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "presentableName", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf("com.intellij.platform.workspace.storage.SymbolicEntityId")) + + addMetadata(typeMetadata) + + typeMetadata = EntityMetadata(fqName = "com.intellij.workspaceModel.test.api.BaseEntity", entityDataFqName = "com.intellij.workspaceModel.test.api.impl.BaseEntityData", supertypes = listOf("com.intellij.platform.workspace.storage.WorkspaceEntity"), properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "entitySource", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntitySource")), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "moduleId", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "presentableName", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf("com.intellij.platform.workspace.storage.SymbolicEntityId"))), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "aBaseEntityProperty", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "dBaseEntityProperty", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "bBaseEntityProperty", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "sealedDataClassProperty", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = ExtendableClassMetadata.AbstractClassMetadata(fqName = "com.intellij.workspaceModel.test.api.BaseDataClass", subclasses = listOf(FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.DerivedDerivedDataClass", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "anotherBaseBodyProperty", valueType = primitiveTypeIntNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "baseBodyProperty", valueType = primitiveTypeIntNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "baseConstructorProperty", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "deriveDerivedBodyProperty", valueType = primitiveTypeIntNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "derivedBodyProperty", valueType = primitiveTypeIntNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "derivedConstructorProperty", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "derivedDerivedConstructorProperty", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf("com.intellij.workspaceModel.test.api.BaseDataClass", +"com.intellij.workspaceModel.test.api.DerivedDataClass"))), supertypes = listOf())), withDefault = false)), extProperties = listOf(), isAbstract = true) + + addMetadata(typeMetadata) + + typeMetadata = EntityMetadata(fqName = "com.intellij.workspaceModel.test.api.ChildEntity", entityDataFqName = "com.intellij.workspaceModel.test.api.impl.ChildEntityData", supertypes = listOf("com.intellij.platform.workspace.storage.WorkspaceEntity", +"com.intellij.workspaceModel.test.api.BaseEntity"), properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "entitySource", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntitySource")), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "moduleId", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "presentableName", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf("com.intellij.platform.workspace.storage.SymbolicEntityId"))), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "aBaseEntityProperty", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "dBaseEntityProperty", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "bBaseEntityProperty", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "sealedDataClassProperty", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = ExtendableClassMetadata.AbstractClassMetadata(fqName = "com.intellij.workspaceModel.test.api.BaseDataClass", subclasses = listOf(FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.DerivedDerivedDataClass", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "anotherBaseBodyProperty", valueType = primitiveTypeIntNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "baseBodyProperty", valueType = primitiveTypeIntNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "baseConstructorProperty", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "deriveDerivedBodyProperty", valueType = primitiveTypeIntNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "derivedBodyProperty", valueType = primitiveTypeIntNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "derivedConstructorProperty", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "derivedDerivedConstructorProperty", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf("com.intellij.workspaceModel.test.api.BaseDataClass", +"com.intellij.workspaceModel.test.api.DerivedDataClass"))), supertypes = listOf())), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "cChildEntityProperty", valueType = primitiveTypeStringNotNullable, withDefault = false)), extProperties = listOf(), isAbstract = false) + + addMetadata(typeMetadata) + + typeMetadata = EntityMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleEntity", entityDataFqName = "com.intellij.workspaceModel.test.api.impl.SimpleEntityData", supertypes = listOf("com.intellij.platform.workspace.storage.WorkspaceEntity", +"com.intellij.platform.workspace.storage.WorkspaceEntityWithSymbolicId"), properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "entitySource", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntitySource")), withDefault = false), +OwnPropertyMetadata(isComputable = true, isKey = false, isOpen = false, name = "symbolicId", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "presentableName", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf("com.intellij.platform.workspace.storage.SymbolicEntityId"))), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false)), extProperties = listOf(), isAbstract = false) + + addMetadata(typeMetadata) + } + + override fun initializeMetadataHash() { + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.BaseEntity", metadataHash = -433943746) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.ChildEntity", metadataHash = -296025373) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SimpleEntity", metadataHash = -2018622680) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SimpleId", metadataHash = 1096965603) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.BaseDataClass", metadataHash = 937384310) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.DerivedDataClass", metadataHash = 1525344553) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.DerivedDerivedDataClass", metadataHash = -597612584) + addMetadataHash(typeFqn = "com.intellij.platform.workspace.storage.SymbolicEntityId", metadataHash = -1044278955) + } + +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/gen/impl/SimpleEntityImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/gen/impl/SimpleEntityImpl.kt new file mode 100644 index 000000000000..32ebdb44d5ca --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/after/gen/impl/SimpleEntityImpl.kt @@ -0,0 +1,192 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.workspaceModel.test.api.impl + +import com.intellij.platform.workspace.storage.* +import com.intellij.platform.workspace.storage.annotations.Abstract +import com.intellij.platform.workspace.storage.impl.ModifiableWorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.WorkspaceEntityData +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentation +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentationApi +import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata +import com.intellij.workspaceModel.test.api.SimpleEntity +import com.intellij.workspaceModel.test.api.SimpleId + +@GeneratedCodeApiVersion(3) +@GeneratedCodeImplVersion(6) +@OptIn(WorkspaceEntityInternalApi::class) +internal class SimpleEntityImpl(private val dataSource: SimpleEntityData) : SimpleEntity, WorkspaceEntityBase(dataSource) { + + private companion object { + + + private val connections = listOf( + ) + + } + + override val symbolicId: SimpleId = super.symbolicId + + override val name: String + get() { + readField("name") + return dataSource.name + } + + override val entitySource: EntitySource + get() { + readField("entitySource") + return dataSource.entitySource + } + + override fun connectionIdList(): List { + return connections + } + + + internal class Builder(result: SimpleEntityData?) : ModifiableWorkspaceEntityBase(result), + SimpleEntity.Builder { + internal constructor() : this(SimpleEntityData()) + + override fun applyToBuilder(builder: MutableEntityStorage) { + if (this.diff != null) { + if (existsInBuilder(builder)) { + this.diff = builder + return + } + else { + error("Entity SimpleEntity is already created in a different builder") + } + } + + this.diff = builder + addToBuilder() + this.id = getEntityData().createEntityId() + // After adding entity data to the builder, we need to unbind it and move the control over entity data to builder + // Builder may switch to snapshot at any moment and lock entity data to modification + this.currentEntityData = null + + // Process linked entities that are connected without a builder + processLinkedEntities(builder) + checkInitialization() // TODO uncomment and check failed tests + } + + private fun checkInitialization() { + val _diff = diff + if (!getEntityData().isEntitySourceInitialized()) { + error("Field WorkspaceEntity#entitySource should be initialized") + } + if (!getEntityData().isNameInitialized()) { + error("Field SimpleEntity#name should be initialized") + } + } + + override fun connectionIdList(): List { + return connections + } + + // Relabeling code, move information from dataSource to this builder + override fun relabel(dataSource: WorkspaceEntity, parents: Set?) { + dataSource as SimpleEntity + if (this.entitySource != dataSource.entitySource) this.entitySource = dataSource.entitySource + if (this.name != dataSource.name) this.name = dataSource.name + updateChildToParentReferences(parents) + } + + + override var entitySource: EntitySource + get() = getEntityData().entitySource + set(value) { + checkModificationAllowed() + getEntityData(true).entitySource = value + changedProperty.add("entitySource") + + } + + override var name: String + get() = getEntityData().name + set(value) { + checkModificationAllowed() + getEntityData(true).name = value + changedProperty.add("name") + } + + override fun getEntityClass(): Class = SimpleEntity::class.java + } +} + +@OptIn(WorkspaceEntityInternalApi::class) +internal class SimpleEntityData : WorkspaceEntityData() { + lateinit var name: String + + internal fun isNameInitialized(): Boolean = ::name.isInitialized + + override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntity.Builder { + val modifiable = SimpleEntityImpl.Builder(null) + modifiable.diff = diff + modifiable.id = createEntityId() + return modifiable + } + + @OptIn(EntityStorageInstrumentationApi::class) + override fun createEntity(snapshot: EntityStorageInstrumentation): SimpleEntity { + val entityId = createEntityId() + return snapshot.initializeEntity(entityId) { + val entity = SimpleEntityImpl(this) + entity.snapshot = snapshot + entity.id = entityId + entity + } + } + + override fun getMetadata(): EntityMetadata { + return MetadataStorageImpl.getMetadataByTypeFqn("com.intellij.workspaceModel.test.api.SimpleEntity") as EntityMetadata + } + + override fun getEntityInterface(): Class { + return SimpleEntity::class.java + } + + override fun createDetachedEntity(parents: List>): WorkspaceEntity.Builder<*> { + return SimpleEntity(name, entitySource) { + } + } + + override fun getRequiredParents(): List> { + val res = mutableListOf>() + return res + } + + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as SimpleEntityData + + if (this.entitySource != other.entitySource) return false + if (this.name != other.name) return false + return true + } + + override fun equalsIgnoringEntitySource(other: Any?): Boolean { + if (other == null) return false + if (this.javaClass != other.javaClass) return false + + other as SimpleEntityData + + if (this.name != other.name) return false + return true + } + + override fun hashCode(): Int { + var result = entitySource.hashCode() + result = 31 * result + name.hashCode() + return result + } + + override fun hashCodeIgnoringEntitySource(): Int { + var result = javaClass.hashCode() + result = 31 * result + name.hashCode() + return result + } +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/before/entity.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/before/entity.kt new file mode 100644 index 000000000000..0c51f936fd7e --- /dev/null +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/propertiesOrder/before/entity.kt @@ -0,0 +1,55 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.workspaceModel.test.api + +import com.intellij.platform.workspace.storage.* +import com.intellij.platform.workspace.storage.annotations.Abstract + +interface SimpleEntity : WorkspaceEntityWithSymbolicId { + override val symbolicId: SimpleId + get() = SimpleId(name) + + val name: String +} + +data class SimpleId(val name: String) : SymbolicEntityId { + override val presentableName: String + get() = name +} + +// partial copy of org.jetbrains.kotlin.idea.workspaceModel.KotlinSettingsEntity +@Abstract +interface BaseEntity : WorkspaceEntity { + val name: String + val moduleId: SimpleId + + val aBaseEntityProperty: String + val dBaseEntityProperty: String + val bBaseEntityProperty: String + + val sealedDataClassProperty: BaseDataClass +} + +interface ChildEntity : BaseEntity { + val cChildEntityProperty: String +} + +sealed class BaseDataClass(val baseConstructorProperty: String) { + open val baseBodyProperty: Int = 15 + val anotherBaseBodyProperty: Int = 16 +} + +sealed class DerivedDataClass( + baseConstructorPropertyValue: String, + val derivedConstructorProperty: String +) : BaseDataClass(baseConstructorPropertyValue) { + val derivedBodyProperty: Int = 23 +} + +class DerivedDerivedDataClass( + baseConstructorPropertyValue: String, + override val baseBodyProperty: Int, + derivedConstructorPropertyValue: String, + val derivedDerivedConstructorProperty: String +) : DerivedDataClass(baseConstructorPropertyValue, derivedConstructorPropertyValue) { + val deriveDerivedBodyProperty: Int = 42 +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/refsFromAnotherModule/after/entity.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/refsFromAnotherModule/after/entity.kt index 65ad0e8fbe8e..53c204d29c0c 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/refsFromAnotherModule/after/entity.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/refsFromAnotherModule/after/entity.kt @@ -1,11 +1,11 @@ package com.intellij.workspaceModel.test.api +import com.intellij.platform.workspace.jps.entities.ContentRootEntity import com.intellij.platform.workspace.storage.EntitySource import com.intellij.platform.workspace.storage.EntityType import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion import com.intellij.platform.workspace.storage.MutableEntityStorage import com.intellij.platform.workspace.storage.WorkspaceEntity -import com.intellij.platform.workspace.jps.entities.ContentRootEntity import com.intellij.platform.workspace.storage.annotations.Child interface ReferredEntity : WorkspaceEntity { diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/after/entity.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/after/entity.kt index c3f55f43b815..2bfa2bd41f4c 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/after/entity.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/after/entity.kt @@ -4,8 +4,7 @@ import com.intellij.platform.workspace.storage.EntitySource import com.intellij.platform.workspace.storage.EntityType import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion import com.intellij.platform.workspace.storage.MutableEntityStorage -import com.intellij.platform.workspace.storage.annotations.Default -import com.intellij.platform.workspace.storage.PersistentEntityId +import com.intellij.platform.workspace.storage.SymbolicEntityId import com.intellij.platform.workspace.storage.WorkspaceEntity import com.intellij.platform.workspace.storage.WorkspaceEntityWithSymbolicId @@ -75,5 +74,4 @@ sealed class SealedClassWithLinks { data class Ordered(val list: List) : Many() data class Unordered(val set: Set) : Many() } - -} \ No newline at end of file +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/after/gen/impl/MetadataStorageImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/after/gen/impl/MetadataStorageImpl.kt index 4ed1f60c59d6..d6942658aae0 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/after/gen/impl/MetadataStorageImpl.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/after/gen/impl/MetadataStorageImpl.kt @@ -12,41 +12,47 @@ import com.intellij.platform.workspace.storage.metadata.model.ValueTypeMetadata @OptIn(WorkspaceEntityInternalApi::class) internal object MetadataStorageImpl: MetadataStorageBase() { override fun initializeMetadata() { - val primitiveTypeIntNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "Int") val primitiveTypeStringNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "String") + val primitiveTypeIntNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "Int") val primitiveTypeListNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "List") val primitiveTypeSetNotNullable = ValueTypeMetadata.SimpleType.PrimitiveType(isNullable = false, type = "Set") var typeMetadata: StorageTypeMetadata + typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "presentableName", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf("com.intellij.platform.workspace.storage.SymbolicEntityId")) + + addMetadata(typeMetadata) + typeMetadata = EntityMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleSymbolicIdEntity", entityDataFqName = "com.intellij.workspaceModel.test.api.impl.SimpleSymbolicIdEntityData", supertypes = listOf("com.intellij.platform.workspace.storage.WorkspaceEntity", "com.intellij.platform.workspace.storage.WorkspaceEntityWithSymbolicId"), properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "entitySource", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.platform.workspace.storage.EntitySource")), withDefault = false), OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "version", valueType = primitiveTypeIntNotNullable, withDefault = false), OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), -OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "related", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "presentableName", valueType = primitiveTypeStringNotNullable, withDefault = false), -OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf())), withDefault = false), -OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "sealedClassWithLinks", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = ExtendableClassMetadata.AbstractClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SealedClassWithLinks", subclasses = listOf(FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Many\$Ordered", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "list", valueType = ValueTypeMetadata.ParameterizedType(generics = listOf(ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "presentableName", valueType = primitiveTypeStringNotNullable, withDefault = false), -OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf()))), primitive = primitiveTypeListNotNullable), withDefault = false)), supertypes = listOf("com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Many", -"com.intellij.workspaceModel.test.api.SealedClassWithLinks")), -FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Many\$Unordered", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "set", valueType = ValueTypeMetadata.ParameterizedType(generics = listOf(ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.workspaceModel.test.api.SimpleId"))), primitive = primitiveTypeSetNotNullable), withDefault = false)), supertypes = listOf("com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Many", -"com.intellij.workspaceModel.test.api.SealedClassWithLinks")), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "related", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "presentableName", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf("com.intellij.platform.workspace.storage.SymbolicEntityId"))), withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "sealedClassWithLinks", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = ExtendableClassMetadata.AbstractClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SealedClassWithLinks", subclasses = listOf(FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Many\$Ordered", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "list", valueType = ValueTypeMetadata.ParameterizedType(generics = listOf(ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "presentableName", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf("com.intellij.platform.workspace.storage.SymbolicEntityId")))), primitive = primitiveTypeListNotNullable), withDefault = false)), supertypes = listOf("com.intellij.workspaceModel.test.api.SealedClassWithLinks", +"com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Many")), +FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Many\$Unordered", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "set", valueType = ValueTypeMetadata.ParameterizedType(generics = listOf(ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.workspaceModel.test.api.SimpleId"))), primitive = primitiveTypeSetNotNullable), withDefault = false)), supertypes = listOf("com.intellij.workspaceModel.test.api.SealedClassWithLinks", +"com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Many")), FinalClassMetadata.ObjectMetadata(fqName = "com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Nothing", properties = listOf(), supertypes = listOf("com.intellij.workspaceModel.test.api.SealedClassWithLinks")), FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Single", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "id", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.KnownClass(fqName = "com.intellij.workspaceModel.test.api.SimpleId")), withDefault = false)), supertypes = listOf("com.intellij.workspaceModel.test.api.SealedClassWithLinks"))), supertypes = listOf())), withDefault = false), -OwnPropertyMetadata(isComputable = true, isKey = false, isOpen = false, name = "symbolicId", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "presentableName", valueType = primitiveTypeStringNotNullable, withDefault = false), -OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf())), withDefault = false)), extProperties = listOf(), isAbstract = false) +OwnPropertyMetadata(isComputable = true, isKey = false, isOpen = false, name = "symbolicId", valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false, typeMetadata = FinalClassMetadata.ClassMetadata(fqName = "com.intellij.workspaceModel.test.api.SimpleId", properties = listOf(OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "name", valueType = primitiveTypeStringNotNullable, withDefault = false), +OwnPropertyMetadata(isComputable = false, isKey = false, isOpen = false, name = "presentableName", valueType = primitiveTypeStringNotNullable, withDefault = false)), supertypes = listOf("com.intellij.platform.workspace.storage.SymbolicEntityId"))), withDefault = false)), extProperties = listOf(), isAbstract = false) addMetadata(typeMetadata) } override fun initializeMetadataHash() { - addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SimpleSymbolicIdEntity", metadataHash = 920886074) - addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SimpleId", metadataHash = 503726574) - addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SealedClassWithLinks", metadataHash = 869203958) - addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Many", metadataHash = 1388134395) - addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Many\$Ordered", metadataHash = -2032838886) - addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Many\$Unordered", metadataHash = -1543556527) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SimpleSymbolicIdEntity", metadataHash = -99546289) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SimpleId", metadataHash = 1096965603) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SealedClassWithLinks", metadataHash = 229222369) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Many", metadataHash = 372382182) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Many\$Ordered", metadataHash = -1632944095) + addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Many\$Unordered", metadataHash = -585653981) addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Nothing", metadataHash = 1541593304) addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SealedClassWithLinks\$Single", metadataHash = -800484835) + addMetadataHash(typeFqn = "com.intellij.platform.workspace.storage.SymbolicEntityId", metadataHash = -1044278955) } } diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/after/gen/impl/SimpleSymbolicIdEntityImpl.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/after/gen/impl/SimpleSymbolicIdEntityImpl.kt index 6769454c5408..e6515f69f43d 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/after/gen/impl/SimpleSymbolicIdEntityImpl.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/after/gen/impl/SimpleSymbolicIdEntityImpl.kt @@ -5,14 +5,15 @@ import com.intellij.platform.workspace.storage.EntitySource import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion import com.intellij.platform.workspace.storage.GeneratedCodeImplVersion import com.intellij.platform.workspace.storage.MutableEntityStorage -import com.intellij.platform.workspace.storage.PersistentEntityId +import com.intellij.platform.workspace.storage.SymbolicEntityId import com.intellij.platform.workspace.storage.WorkspaceEntity import com.intellij.platform.workspace.storage.WorkspaceEntityInternalApi import com.intellij.platform.workspace.storage.WorkspaceEntityWithSymbolicId -import com.intellij.platform.workspace.storage.annotations.Default import com.intellij.platform.workspace.storage.impl.ModifiableWorkspaceEntityBase +import com.intellij.platform.workspace.storage.impl.SoftLinkable import com.intellij.platform.workspace.storage.impl.WorkspaceEntityBase import com.intellij.platform.workspace.storage.impl.WorkspaceEntityData +import com.intellij.platform.workspace.storage.impl.indices.WorkspaceMutableIndex import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentation import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentationApi import com.intellij.platform.workspace.storage.metadata.model.EntityMetadata @@ -177,7 +178,7 @@ internal class SimpleSymbolicIdEntityImpl(private val dataSource: SimpleSymbolic } @OptIn(WorkspaceEntityInternalApi::class) -internal class SimpleSymbolicIdEntityData : WorkspaceEntityData() { +internal class SimpleSymbolicIdEntityData : WorkspaceEntityData(), SoftLinkable { var version: Int = 0 lateinit var name: String lateinit var related: SimpleId @@ -188,6 +189,194 @@ internal class SimpleSymbolicIdEntityData : WorkspaceEntityData> { + val result = HashSet>() + result.add(related) + val _sealedClassWithLinks = sealedClassWithLinks + when (_sealedClassWithLinks) { + is SealedClassWithLinks.Many -> { + val __sealedClassWithLinks = _sealedClassWithLinks + when (__sealedClassWithLinks) { + is SealedClassWithLinks.Many.Ordered -> { + for (item in __sealedClassWithLinks.list) { + result.add(item) + } + } + is SealedClassWithLinks.Many.Unordered -> { + for (item in __sealedClassWithLinks.set) { + result.add(item) + } + } + } + } + is SealedClassWithLinks.Nothing -> { + } + is SealedClassWithLinks.Single -> { + result.add(_sealedClassWithLinks.id) + } + } + return result + } + + override fun index(index: WorkspaceMutableIndex>) { + index.index(this, related) + val _sealedClassWithLinks = sealedClassWithLinks + when (_sealedClassWithLinks) { + is SealedClassWithLinks.Many -> { + val __sealedClassWithLinks = _sealedClassWithLinks + when (__sealedClassWithLinks) { + is SealedClassWithLinks.Many.Ordered -> { + for (item in __sealedClassWithLinks.list) { + index.index(this, item) + } + } + is SealedClassWithLinks.Many.Unordered -> { + for (item in __sealedClassWithLinks.set) { + index.index(this, item) + } + } + } + } + is SealedClassWithLinks.Nothing -> { + } + is SealedClassWithLinks.Single -> { + index.index(this, _sealedClassWithLinks.id) + } + } + } + + override fun updateLinksIndex(prev: Set>, index: WorkspaceMutableIndex>) { + // TODO verify logic + val mutablePreviousSet = HashSet(prev) + val removedItem_related = mutablePreviousSet.remove(related) + if (!removedItem_related) { + index.index(this, related) + } + val _sealedClassWithLinks = sealedClassWithLinks + when (_sealedClassWithLinks) { + is SealedClassWithLinks.Many -> { + val __sealedClassWithLinks = _sealedClassWithLinks + when (__sealedClassWithLinks) { + is SealedClassWithLinks.Many.Ordered -> { + for (item in __sealedClassWithLinks.list) { + val removedItem_item = mutablePreviousSet.remove(item) + if (!removedItem_item) { + index.index(this, item) + } + } + } + is SealedClassWithLinks.Many.Unordered -> { + for (item in __sealedClassWithLinks.set) { + val removedItem_item = mutablePreviousSet.remove(item) + if (!removedItem_item) { + index.index(this, item) + } + } + } + } + } + is SealedClassWithLinks.Nothing -> { + } + is SealedClassWithLinks.Single -> { + val removedItem__sealedClassWithLinks_id = mutablePreviousSet.remove(_sealedClassWithLinks.id) + if (!removedItem__sealedClassWithLinks_id) { + index.index(this, _sealedClassWithLinks.id) + } + } + } + for (removed in mutablePreviousSet) { + index.remove(this, removed) + } + } + + override fun updateLink(oldLink: SymbolicEntityId<*>, newLink: SymbolicEntityId<*>): Boolean { + var changed = false + val related_data = if (related == oldLink) { + changed = true + newLink as SimpleId + } + else { + null + } + if (related_data != null) { + related = related_data + } + val _sealedClassWithLinks = sealedClassWithLinks + val res_sealedClassWithLinks = when (_sealedClassWithLinks) { + is SealedClassWithLinks.Many -> { + val __sealedClassWithLinks = _sealedClassWithLinks + val res__sealedClassWithLinks = when (__sealedClassWithLinks) { + is SealedClassWithLinks.Many.Ordered -> { + val __sealedClassWithLinks_list_data = __sealedClassWithLinks.list.map { + val it_data = if (it == oldLink) { + changed = true + newLink as SimpleId + } + else { + null + } + if (it_data != null) { + it_data + } + else { + it + } + } + var __sealedClassWithLinks_data = __sealedClassWithLinks + if (__sealedClassWithLinks_list_data != null) { + __sealedClassWithLinks_data = __sealedClassWithLinks_data.copy(list = __sealedClassWithLinks_list_data) + } + __sealedClassWithLinks_data + } + is SealedClassWithLinks.Many.Unordered -> { + val __sealedClassWithLinks_set_data = __sealedClassWithLinks.set.map { + val it_data = if (it == oldLink) { + changed = true + newLink as SimpleId + } + else { + null + } + if (it_data != null) { + it_data + } + else { + it + } + } + var __sealedClassWithLinks_data = __sealedClassWithLinks + if (__sealedClassWithLinks_set_data != null) { + __sealedClassWithLinks_data = __sealedClassWithLinks_data.copy(set = __sealedClassWithLinks_set_data) + } + __sealedClassWithLinks_data + } + } + res__sealedClassWithLinks + } + is SealedClassWithLinks.Nothing -> { + _sealedClassWithLinks + } + is SealedClassWithLinks.Single -> { + val _sealedClassWithLinks_id_data = if (_sealedClassWithLinks.id == oldLink) { + changed = true + newLink as SimpleId + } + else { + null + } + var _sealedClassWithLinks_data = _sealedClassWithLinks + if (_sealedClassWithLinks_id_data != null) { + _sealedClassWithLinks_data = _sealedClassWithLinks_data.copy(id = _sealedClassWithLinks_id_data) + } + _sealedClassWithLinks_data + } + } + if (res_sealedClassWithLinks != null) { + sealedClassWithLinks = res_sealedClassWithLinks + } + return changed + } + override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntity.Builder { val modifiable = SimpleSymbolicIdEntityImpl.Builder(null) modifiable.diff = diff diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/before/entity.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/before/entity.kt index 8057343cadff..d3ebf6537e4a 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/before/entity.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/symbolicId/before/entity.kt @@ -1,8 +1,6 @@ package com.intellij.workspaceModel.test.api -import com.intellij.platform.workspace.storage.annotations.Default -import com.intellij.platform.workspace.storage.PersistentEntityId -import com.intellij.platform.workspace.storage.WorkspaceEntity +import com.intellij.platform.workspace.storage.SymbolicEntityId import com.intellij.platform.workspace.storage.WorkspaceEntityWithSymbolicId interface SimpleSymbolicIdEntity : WorkspaceEntityWithSymbolicId { @@ -28,5 +26,4 @@ sealed class SealedClassWithLinks { data class Ordered(val list: List) : Many() data class Unordered(val set: Set) : Many() } - -} \ No newline at end of file +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/com/intellij/devkit/workspaceModel/AllIntellijEntitiesGenerationTest.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/AbstractAllIntellijEntitiesGenerationTest.kt similarity index 93% rename from plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/com/intellij/devkit/workspaceModel/AllIntellijEntitiesGenerationTest.kt rename to plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/AbstractAllIntellijEntitiesGenerationTest.kt index fc5e57f14255..348737ff360a 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/com/intellij/devkit/workspaceModel/AllIntellijEntitiesGenerationTest.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/AbstractAllIntellijEntitiesGenerationTest.kt @@ -8,6 +8,7 @@ import com.intellij.java.workspace.entities.JavaSourceRootPropertiesEntity import com.intellij.java.workspace.entities.javaSourceRoots import com.intellij.openapi.application.runWriteActionAndWait import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.fileTypes.LanguageFileType import com.intellij.openapi.roots.ModifiableRootModel import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.util.io.FileUtil @@ -46,8 +47,8 @@ import java.nio.file.Paths import kotlin.io.path.invariantSeparatorsPathString import kotlin.io.path.pathString -class AllIntellijEntitiesGenerationTest : CodeGenerationTestBase() { - private val LOG = logger() +abstract class AbstractAllIntellijEntitiesGenerationTest : CodeGenerationTestBase() { + private val LOG = logger() private val virtualFileManager = IdeVirtualFileUrlManagerImpl() @@ -75,6 +76,7 @@ class AllIntellijEntitiesGenerationTest : CodeGenerationTestBase() { override val testDataDirectory: File get() = File(IdeaTestExecutionPolicy.getHomePathWithPolicy()) + // IJPL-895 fun `_test generation of all entities in intellij codebase`() { executeEntitiesGeneration(::generateAndCompare) } @@ -148,8 +150,10 @@ class AllIntellijEntitiesGenerationTest : CodeGenerationTestBase() { modulesToCheck.forEach { (moduleEntity, sourceRoot), pathToPackages -> val isTestModule = sourceRoot.rootTypeId == JAVA_TEST_ROOT_ENTITY_TYPE_ID - fun generateForSpecificModule(processAbstractTypes: Boolean, beforeGeneration: (ModifiableRootModel) -> Unit, - afterGeneration: (ModifiableRootModel) -> Unit) { + fun generateForSpecificModule( + processAbstractTypes: Boolean, beforeGeneration: (ModifiableRootModel) -> Unit, + afterGeneration: (ModifiableRootModel) -> Unit, + ) { runWriteActionAndWait { val modifiableModel = ModuleRootManager.getInstance(module).modifiableModel beforeGeneration(modifiableModel) @@ -165,11 +169,11 @@ class AllIntellijEntitiesGenerationTest : CodeGenerationTestBase() { } when (moduleEntity.name) { - "intellij.platform.workspace.storage"-> { + "intellij.platform.workspace.storage" -> { generateForSpecificModule(false, { modifiableModel -> removeWorkspaceStorageLibrary(modifiableModel) }, { modifiableModel -> addWorkspaceStorageLibrary(modifiableModel) }) } - "intellij.platform.workspace.jps"-> { + "intellij.platform.workspace.jps" -> { generateForSpecificModule(false, { modifiableModel -> removeWorkspaceJpsEntitiesLibrary(modifiableModel) }, { modifiableModel -> addWorkspaceJpsEntitiesLibrary(modifiableModel) }) } @@ -191,9 +195,9 @@ class AllIntellijEntitiesGenerationTest : CodeGenerationTestBase() { addIntellijJavaLibrary(modifiableModel) addKotlinJpsCommonJar(modifiableModel) }, { modifiableModel -> - removeIntellijJavaLibrary(modifiableModel) - removeKotlinJpsCommonJar(modifiableModel) - }) + removeIntellijJavaLibrary(modifiableModel) + removeKotlinJpsCommonJar(modifiableModel) + }) } in modulesWithAbstractTypes -> { val projectModelUpdateResult = generationFunction(storage, moduleEntity, sourceRoot, pathToPackages, true, false, isTestModule) @@ -216,7 +220,7 @@ class AllIntellijEntitiesGenerationTest : CodeGenerationTestBase() { storage: MutableEntityStorage, moduleEntity: ModuleEntity, sourceRoot: SourceRootEntity, pathToPackages: Set, processAbstractTypes: Boolean, explicitApiEnabled: Boolean, - isTestModule: Boolean + isTestModule: Boolean, ): Boolean { val relativize = Path.of(IdeaTestExecutionPolicy.getHomePathWithPolicy()).relativize(Path.of(sourceRoot.url.presentableUrl)) myFixture.copyDirectoryToProject(relativize.invariantSeparatorsPathString, "") @@ -272,7 +276,7 @@ class AllIntellijEntitiesGenerationTest : CodeGenerationTestBase() { storage: MutableEntityStorage, moduleEntity: ModuleEntity, sourceRoot: SourceRootEntity, pathToPackages: Set, processAbstractTypes: Boolean, explicitApiEnabled: Boolean, - isTestModule: Boolean + isTestModule: Boolean, ): Boolean { val genSourceRoots = sourceRoot.contentRoot.sourceRoots.flatMap { it.javaSourceRoots }.filter { it.generated } val relativize = Path.of(IdeaTestExecutionPolicy.getHomePathWithPolicy()).relativize(Path.of(sourceRoot.url.presentableUrl)) @@ -314,7 +318,8 @@ class AllIntellijEntitiesGenerationTest : CodeGenerationTestBase() { private fun setupCustomIndent(moduleEntity: ModuleEntity) { val indent = modulesWithCustomIndentSize[moduleEntity.name] ?: return val settings = CodeStyle.getSettings(project) - val indentOptions = settings.getIndentOptions(KotlinFileType.INSTANCE) + val kotlinFileType: LanguageFileType? = KotlinFileType.INSTANCE + val indentOptions = settings.getIndentOptions(kotlinFileType) indentOptions.INDENT_SIZE = indent indentOptions.TAB_SIZE = indent indentOptions.CONTINUATION_INDENT_SIZE = indent @@ -323,7 +328,8 @@ class AllIntellijEntitiesGenerationTest : CodeGenerationTestBase() { private fun resetCustomIndent(moduleEntity: ModuleEntity) { modulesWithCustomIndentSize[moduleEntity.name] ?: return val settings = CodeStyle.getSettings(project) - val indentOptions = settings.getIndentOptions(KotlinFileType.INSTANCE) + val kotlinFileType: LanguageFileType? = KotlinFileType.INSTANCE + val indentOptions = settings.getIndentOptions(kotlinFileType) indentOptions.INDENT_SIZE = INDENT_SIZE indentOptions.TAB_SIZE = TAB_SIZE indentOptions.CONTINUATION_INDENT_SIZE = CONTINUATION_INDENT_SIZE @@ -348,14 +354,11 @@ class AllIntellijEntitiesGenerationTest : CodeGenerationTestBase() { return filter { path -> none { anotherPath -> anotherPath != path && anotherPath.isSubPathOf(path) } } } - private fun Path.isSubPathOf(anotherPath: Path): Boolean - = anotherPath.startsWith(this) - - + private fun Path.isSubPathOf(anotherPath: Path): Boolean = anotherPath.startsWith(this) + internal object TestErrorReporter : ErrorReporter { override fun reportError(message: String, file: VirtualFileUrl) { throw AssertionFailedError("Failed to load ${file.url}: $message") } } -} - +} \ No newline at end of file diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/com/intellij/devkit/workspaceModel/EntityCodeGenTest.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/AbstractEntityCodeGenTest.kt similarity index 90% rename from plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/com/intellij/devkit/workspaceModel/EntityCodeGenTest.kt rename to plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/AbstractEntityCodeGenTest.kt index 82449bc1ef1c..8e7a75199e00 100644 --- a/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/com/intellij/devkit/workspaceModel/EntityCodeGenTest.kt +++ b/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/AbstractEntityCodeGenTest.kt @@ -2,11 +2,11 @@ package com.intellij.devkit.workspaceModel import com.intellij.openapi.application.ex.PathManagerEx +import org.junit.jupiter.api.Assertions import java.io.File import java.nio.file.Path -import org.junit.jupiter.api.Assertions -class EntityCodeGenTest : CodeGenerationTestBase() { +abstract class AbstractEntityCodeGenTest : CodeGenerationTestBase() { override val testDataDirectory: File get() = File(PathManagerEx.getCommunityHomePath() + "/plugins/devkit/intellij.devkit.workspaceModel/tests/testData/codeGen/$testDirectoryName") @@ -39,6 +39,14 @@ class EntityCodeGenTest : CodeGenerationTestBase() { doTest() } + fun testEntityWithChildrenCollection() { + doTest() + } + + fun testEntityWithDifferentChildrenTargets() { + doTest() + } + fun testRefsFromAnotherModule() { doTest() } @@ -63,6 +71,14 @@ class EntityCodeGenTest : CodeGenerationTestBase() { doTest(processAbstractTypes = true) } + fun testImports() { + doTest() + } + + fun testPropertiesOrder() { + doTest() + } + fun testBothLinksAreParents() { doTestAndCheckErrorMessage("Both fields MainEntity#secondaryEntities and SecondaryEntity#mainEntity are marked as parent. Did you forget to add @Child annotation?") } @@ -97,8 +113,7 @@ class EntityCodeGenTest : CodeGenerationTestBase() { ) } - private fun getExpectedDir(): Path { return testDataDirectory.toPath().resolve("after") } -} \ No newline at end of file +} diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/com/intellij/devkit/workspaceModel/CodeGenerationTestBase.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/CodeGenerationTestBase.kt similarity index 100% rename from plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/com/intellij/devkit/workspaceModel/CodeGenerationTestBase.kt rename to plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/CodeGenerationTestBase.kt diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/com/intellij/devkit/workspaceModel/inspections/WorkspaceEntityInspectionBase.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/inspections/WorkspaceEntityInspectionBase.kt similarity index 100% rename from plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/com/intellij/devkit/workspaceModel/inspections/WorkspaceEntityInspectionBase.kt rename to plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/inspections/WorkspaceEntityInspectionBase.kt diff --git a/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/com/intellij/devkit/workspaceModel/inspections/WorkspaceEntityMutableFieldInspectionTest.kt b/plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/inspections/WorkspaceEntityMutableFieldInspectionTest.kt similarity index 100% rename from plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/com/intellij/devkit/workspaceModel/inspections/WorkspaceEntityMutableFieldInspectionTest.kt rename to plugins/devkit/intellij.devkit.workspaceModel/tests/testSrc/inspections/WorkspaceEntityMutableFieldInspectionTest.kt diff --git a/plugins/devkit/intellij.devkit/plugin-content.yaml b/plugins/devkit/intellij.devkit/plugin-content.yaml index 5cf020b4ec29..dfbfdb1791ed 100644 --- a/plugins/devkit/intellij.devkit/plugin-content.yaml +++ b/plugins/devkit/intellij.devkit/plugin-content.yaml @@ -22,6 +22,8 @@ - name: intellij.devkit.themes - name: intellij.devkit.uiDesigner - name: intellij.devkit.workspaceModel + - name: intellij.devkit.workspaceModel.k1 + - name: intellij.devkit.workspaceModel.k2 - name: intellij.kotlin.devkit - name: intellij.kotlin.devkit.k1 - name: intellij.kotlin.devkit.k2 diff --git a/plugins/devkit/intellij.devkit/resources/META-INF/plugin.xml b/plugins/devkit/intellij.devkit/resources/META-INF/plugin.xml index 7ca5fe15f5e4..9543c6783469 100644 --- a/plugins/devkit/intellij.devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/intellij.devkit/resources/META-INF/plugin.xml @@ -26,6 +26,8 @@ + +