IJPL-895 Workspace Model: codegen fixes and improvements

- copyright for Metadata and Modifications.kt
- fix deprecations
- remove old code related to modifying sources
- skip generated source roots
- optimize imports in generated code
- doPostponedOperationsAndUnblockDocument
- dependencies for intellij.rd.ide.model.generated, intellij.android.projectSystem.gradle
- IJPL-212407 Ignore NonPersistentEntitySource in generator


Merge-request: IJ-MR-186285
Merged-by: Kirill Bochkarev <kirill.bochkarev@jetbrains.com>

GitOrigin-RevId: 2cb825d65b195612dbc24b614663724ed1528888
This commit is contained in:
Kirill Bochkarev
2025-12-17 22:50:15 +00:00
committed by intellij-monorepo-bot
parent 38c69cf8d3
commit 33ebfaabc0
44 changed files with 242 additions and 1239 deletions
@@ -9,7 +9,7 @@ public object CodeGeneratorVersions {
/** these constants are accessed from WorkspaceImplObsoleteInspection */
private const val API_VERSION_INTERNAL = 3
private const val IMPL_MAJOR_VERSION_INTERNAL = 7
private const val IMPL_MINOR_VERSION_INTERNAL = 8
private const val IMPL_MINOR_VERSION_INTERNAL = 9
@set:TestOnly
public var API_VERSION: Int = API_VERSION_INTERNAL
@@ -67,6 +67,7 @@ jvm_library(
"//platform/workspace/jps",
"//java/java-impl:impl",
"//plugins/kotlin/base/psi",
"//platform/code-style-impl:codeStyle-impl",
]
)
### auto-generated section `build intellij.devkit.workspaceModel` end
@@ -64,5 +64,6 @@
<orderEntry type="module" module-name="intellij.platform.workspace.jps" />
<orderEntry type="module" module-name="intellij.java.impl" />
<orderEntry type="module" module-name="intellij.kotlin.base.psi" />
<orderEntry type="module" module-name="intellij.platform.codeStyle.impl" />
</component>
</module>
@@ -157,7 +157,7 @@ internal class WorkspaceMetaModelBuilder(
val blobType = ValueType.Blob<Any>(javaClassFqn, superTypes)
val inheritors = descriptor.inheritors(javaPsiFacade, allScope)
.filter { it.packageName == compiledObjModule.name } // && it.module == moduleDescriptor }
.filter { it.packageName == compiledObjModule.name && it.name.identifier != "NonPersistentEntitySource" }
.map { classDescriptorToValueType(it, hashMapOf(javaClassFqn to blobType), true) }
if (inheritors.isNotEmpty()) {
@@ -18,7 +18,6 @@ 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
@@ -167,8 +166,9 @@ internal class WorkspaceMetaModelBuilder(
for (inheritor in inheritorsKtClasses) {
analyze(inheritor) {
val inheritorSymbol = inheritor.namedClassSymbol
// FIXME: Check for module removed because of problems with kotlin.base.scripting in tests, might liead to other problems
if (inheritorSymbol != null && inheritorSymbol.packageName == compiledObjModule.name) { // && inheritorSymbol.containingModule == this@ObjModuleStub.kaModule ) {
// FIXME: Check for module (inheritorSymbol.containingModule == this@ObjModuleStub.kaModule) removed because of problems with
// kotlin.base.scripting in tests, might lead to other problems.
if (inheritorSymbol != null && inheritorSymbol.packageName == compiledObjModule.name && inheritorSymbol.name.identifier != "NonPersistentEntitySource") {
val inheritorValueType = classSymbolToValueType(inheritorSymbol, hashMapOf(javaClassFqn to blobType), true)
inheritors.add(inheritorValueType)
}
@@ -218,8 +218,8 @@ internal class WorkspaceMetaModelBuilder(
hasParentAnnotation: Boolean,
): ValueType<*> {
if (type !is KaClassType) error("$type is not a class in module ${compiledObjModule.name}")
if (type.nullability == KaTypeNullability.NULLABLE) {
val nonNullableType = type.withNullability(KaTypeNullability.NON_NULLABLE)
if (type.isMarkedNullable) {
val nonNullableType = type.withNullability(false)
return ValueType.Optional(convertType(nonNullableType, knownTypes, hasParentAnnotation))
}
@@ -146,7 +146,8 @@ class WorkspaceModelGenerator(private val project: Project, private val coroutin
const val RIDER_MODULES_PREFIX: String = "intellij.rider"
val modulesWithAbstractTypes: Set<String> = setOf(
"intellij.platform.workspace.storage.testEntities"
"intellij.platform.workspace.storage.testEntities",
"intellij.android.projectSystem.gradle"
)
fun getInstance(project: Project): WorkspaceModelGenerator = project.service()
@@ -5,7 +5,7 @@ import com.intellij.devkit.workspaceModel.CodegenJarLoader
import com.intellij.devkit.workspaceModel.DevKitWorkspaceModelBundle
import com.intellij.devkit.workspaceModel.codegen.writer.CodeWriter.addGeneratedObjModuleFile
import com.intellij.devkit.workspaceModel.metaModel.WorkspaceMetaModelProvider
import com.intellij.lang.ASTNode
import com.intellij.lang.LanguageImportStatements
import com.intellij.notification.NotificationGroupManager
import com.intellij.notification.NotificationType
import com.intellij.openapi.application.PathManager
@@ -23,27 +23,24 @@ import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiManager
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil
import com.intellij.util.concurrency.annotations.RequiresEdt
import com.intellij.util.containers.FactoryMap
import com.intellij.util.containers.MultiMap
import com.intellij.workspaceModel.codegen.deft.meta.CompiledObjModule
import com.intellij.workspaceModel.codegen.engine.*
import kotlinx.coroutines.delay
import org.jetbrains.io.JsonReaderEx
import org.jetbrains.io.JsonUtil
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.children
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.isPublic
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
import org.jetbrains.kotlin.resolve.ImportPath
import java.io.IOException
import java.net.URL
import java.net.URI
import java.nio.file.Path
import java.util.*
import java.util.jar.Manifest
@@ -59,32 +56,27 @@ object CodeWriter {
isTestSourceFolder: Boolean, isTestModule: Boolean,
targetFolderGenerator: () -> VirtualFile?,
existingTargetFolder: () -> VirtualFile?,
formatCode: Boolean
formatCode: Boolean,
) {
val sourceFilePerObjModule = HashMap<String, VirtualFile>()
val ktClasses = HashMap<String, KtClassOrObject>()
readAction {
VfsUtilCore.processFilesRecursively(sourceFolder) {
if (it.extension == "kt") {
val ktFile = PsiManager.getInstance(project).findFile(it) as? KtFile?
VfsUtilCore.processFilesRecursively(sourceFolder) { file ->
if (file.extension == "kt") {
val ktFile = PsiManager.getInstance(project).findFile(file) as? KtFile?
ktFile?.declarations
?.filterIsInstance<KtClassOrObject>()
?.filter { it.name != null }
?.forEach { ktClass ->
val fqName = ktClass.fqName!!.asString()
val objModuleName = fqName
.replace(ktClass.name!!, "")
.substringBeforeLast(".")
ktFile?.declarations?.filterIsInstance<KtClassOrObject>()?.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] = ktClass
}
/**
* 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] = file
ktClasses[fqName] = ktClass
}
}
return@processFilesRecursively true
}
@@ -118,7 +110,7 @@ object CodeWriter {
val genFolder = existingTargetFolder.invoke()
if (genFolder != null) {
indicator.text = DevKitWorkspaceModelBundle.message("progress.text.removing.old.code")
removeGeneratedCode(ktClasses, genFolder)
removeGeneratedCode(genFolder)
}
return@runWriteActionWithCancellableProgressInDispatchThread
}
@@ -129,14 +121,9 @@ object CodeWriter {
return@runWriteActionWithCancellableProgressInDispatchThread
}
// compatibility invoke in FacetEntity and KotlinSettingsEntity
val savedCompatibilityInvokeCode = mutableMapOf<KtClassOrObject, PsiElement>()
saveCompatibilityCode(ktClasses, savedCompatibilityInvokeCode)
indicator.text = DevKitWorkspaceModelBundle.message("progress.text.removing.old.code")
removeGeneratedCode(ktClasses, genFolder)
removeGeneratedCode(genFolder)
val topLevelDeclarations = MultiMap.create<KtFile, Pair<KtClassOrObject, List<KtDeclaration>>>()
val importsByFile = FactoryMap.create<KtFile, Imports> { Imports(it.packageFqName.asString()) }
val generatedFiles = ArrayList<KtFile>()
@@ -157,9 +144,7 @@ object CodeWriter {
addGeneratedObjClassFile(
code, generatedFiles, project,
sourceFolder, genFolder,
ktClasses, importsByFile,
topLevelDeclarations, psiFactory,
savedCompatibilityInvokeCode
ktClasses, importsByFile, psiFactory,
)
}
}
@@ -167,30 +152,20 @@ object CodeWriter {
importsByFile.forEach { (file, imports) ->
addImports(file, imports)
}
if (!formatCode) return@runWriteActionWithCancellableProgressInDispatchThread
val copiedEditorconfig = copyEditorConfigIfIntellij(project, genFolder)
generatedFiles.forEachIndexed { i, file ->
val kotlinImportOptimizer = generatedFiles.firstOrNull()?.let { LanguageImportStatements.INSTANCE.forFile(it) }?.firstOrNull()
for ((i, file) in generatedFiles.withIndex()) {
DumbService.getInstance(project).completeJustSubmittedTasks()
indicator.fraction = 0.25 + 0.7 * i / generatedFiles.size
if (formatCode) {
CodeStyleManager.getInstance(project).reformat(file)
}
file.apiFileNameForImplFile?.let { ktClasses[it] }?.containingKtFile?.let { apiFile -> copyHeaderComment(apiFile, file) }
PsiDocumentManager.getInstance(file.project).doPostponedOperationsAndUnblockDocument(file.viewProvider.document!!)
addCopyright(file, ktClasses)
CodeStyleManager.getInstance(project).reformat(file)
kotlinImportOptimizer?.processFile(file)?.run()
}
copiedEditorconfig?.delete(CodeWriter)
topLevelDeclarations.entrySet().forEach { (file, placeAndDeclarations) ->
val addedElements = ArrayList<KtDeclaration>()
for ((place, declarations) in placeAndDeclarations) {
var nextPlace: PsiElement = place
val newElements = ArrayList<KtDeclaration>()
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)
}
}
}
}
}
@@ -199,11 +174,24 @@ object CodeWriter {
if (!IntelliJProjectUtil.isIntelliJPlatformProject(project)) {
return null
}
val editorconfigFile = VirtualFileManager.getInstance().refreshAndFindFileByNioPath(Path.of(PathManager.getHomePath(), "community", ".editorconfig"))!!
val editorconfigFile =
VirtualFileManager.getInstance().refreshAndFindFileByNioPath(Path.of(PathManager.getHomePath(), "community", ".editorconfig"))!!
val copied = VfsUtil.copyFile(this, editorconfigFile, genFolder)
return copied
}
private fun addCopyright(file: KtFile, ktClasses: HashMap<String, KtClassOrObject>) {
if (file.name == GENERATED_METADATA_STORAGE_FILE) {
val someEntitySourceFile = ktClasses.values.firstOrNull { it.name?.contains("Entity") ?: false }?.containingKtFile
val anySourceFile = someEntitySourceFile ?: ktClasses.values.firstOrNull()?.containingKtFile ?: return
copyHeaderComment(anySourceFile, file)
return
}
val sourceClassName = getSourceClassNameForGeneratedFile(file)
val sourceFile = ktClasses[sourceClassName]?.containingKtFile ?: return
copyHeaderComment(sourceFile, file)
}
/**
* Documentation for [com.intellij.openapi.project.IndexNotReadyException] says that it's enough to run completeJustSubmittedTasks only
* once. However, in practice this is not enough.
@@ -227,12 +215,17 @@ object CodeWriter {
return true
}
val message = if (apiVersionFromDownloadedJar == CodegenApiVersion.UNKNOWN_VERSION || apiVersionInDevkit > apiVersionFromDownloadedJar) {
DevKitWorkspaceModelBundle.message("notification.workspace.incompatible.codegen.api.versions.content.newer", apiVersionInDevkit, apiVersionFromDownloadedJar)
}
else {
DevKitWorkspaceModelBundle.message("notification.workspace.incompatible.codegen.api.versions.content.older", apiVersionInDevkit, apiVersionFromDownloadedJar)
}
val message =
if (apiVersionFromDownloadedJar == CodegenApiVersion.UNKNOWN_VERSION || apiVersionInDevkit > apiVersionFromDownloadedJar) {
DevKitWorkspaceModelBundle.message("notification.workspace.incompatible.codegen.api.versions.content.newer",
apiVersionInDevkit,
apiVersionFromDownloadedJar)
}
else {
DevKitWorkspaceModelBundle.message("notification.workspace.incompatible.codegen.api.versions.content.older",
apiVersionInDevkit,
apiVersionFromDownloadedJar)
}
val groupId = DevKitWorkspaceModelBundle.message("notification.workspace.incompatible.codegen.api.versions")
NotificationGroupManager.getInstance()
@@ -245,7 +238,7 @@ object CodeWriter {
private fun getApiVersionFromJSON(clazz: Class<*>): String {
return getApiVersionFromJarFile(clazz, CodegenApiVersion.JSON_RELATIVE_PATH) { jsonPath ->
URL(jsonPath).openStream().reader().use { reader ->
URI(jsonPath).toURL().openStream().reader().use { reader ->
val jsonReader = JsonReaderEx(reader.readText())
val objects = JsonUtil.nextObject(jsonReader)
objects[CodegenApiVersion.ATTRIBUTE_NAME] as? String
@@ -255,7 +248,7 @@ object CodeWriter {
private fun getApiVersionFromManifest(clazz: Class<*>): String {
return getApiVersionFromJarFile(clazz, CodegenApiVersion.MANIFEST_RELATIVE_PATH) { manifestPath ->
URL(manifestPath).openStream().use {
URI(manifestPath).toURL().openStream().use {
val manifest = Manifest(it)
val attributes = manifest.mainAttributes
attributes.getValue(CodegenApiVersion.ATTRIBUTE_NAME)
@@ -292,21 +285,7 @@ object CodeWriter {
return entitiesImplementations + metadataStorageImplementation
}
private fun saveCompatibilityCode(ktClasses: Map<String, KtClassOrObject>, savedCompatibilityInvokeCode: MutableMap<KtClassOrObject, PsiElement>) {
for (ktClass in ktClasses.values) {
val companionDeclaration = ktClass.body?.declarations?.find { it is KtObjectDeclaration && it.isCompanion() } as? KtObjectDeclaration
?: continue
val compatibilityInvoke = companionDeclaration.declarations.find { it is KtNamedFunction && it.name == "create" && it.annotationEntries.any { it.shortName.toString() == "Deprecated" } }
?: continue
savedCompatibilityInvokeCode[ktClass] = compatibilityInvoke.copy()
}
}
private fun removeGeneratedCode(ktClasses: Map<String, KtClassOrObject>, genFolder: VirtualFile) {
ktClasses.values.flatMapTo(HashSet()) { listOfNotNull(it.containingFile.node, it.body?.node) }.forEach {
removeChildrenInGeneratedRegions(it)
}
private fun removeGeneratedCode(genFolder: VirtualFile) {
//remove generated files, e.g. MetadataStorageImpl.kt
removeFiles(genFolder) {
it.isGeneratedFile
@@ -354,13 +333,9 @@ object CodeWriter {
}
private fun addGeneratedObjClassFile(
code: ObjClassGeneratedCode, generatedFiles: MutableList<KtFile>,
project: Project, sourceFolder: VirtualFile, genFolder: VirtualFile,
ktClasses: Map<String, KtClassOrObject>, importsByFile: MutableMap<KtFile, Imports>,
topLevelDeclarations: MultiMap<KtFile, Pair<KtClassOrObject, List<KtDeclaration>>>, psiFactory: KtPsiFactory,
savedCompatibilityInvokeCode: MutableMap<KtClassOrObject, PsiElement>,
code: ObjClassGeneratedCode, generatedFiles: MutableList<KtFile>, project: Project, sourceFolder: VirtualFile, genFolder: VirtualFile,
ktClasses: Map<String, KtClassOrObject>, importsByFile: MutableMap<KtFile, Imports>, psiFactory: KtPsiFactory,
) {
if (code.target.name in SKIPPED_TYPES) return
val target = code.target
@@ -370,59 +345,51 @@ object CodeWriter {
LOG.warn("Class $apiInterfaceName was not found")
return
}
val apiFile = apiClass.containingKtFile
val apiImports = importsByFile.getValue(apiFile)
addInnerDeclarations(apiClass, code, apiImports, savedCompatibilityInvokeCode)
val apiPackageFqName = apiClass.containingKtFile.packageFqName
val sourceFile = apiClass.containingFile.virtualFile
val targetDirectory = getPsiDirectory(project, genFolder, sourceFolder, sourceFile)
run {
val apiPackageFqnName = apiFile.packageFqName.asString()
val apiPackageFqnName = apiPackageFqName.asString()
val generatedApiImports = Imports(apiPackageFqnName)
apiClass.containingKtFile.importDirectives.mapNotNull { it.importPath }.forEach { import ->
generatedApiImports.add(import.pathStr)
}
val psiFactory = KtPsiFactory(apiClass.project)
val topLevelCode = code.topLevelCode ?: return@run
val filename = "${code.target.name}Modifications"
val generatedApiFile = psiFactory.createFile("$filename.kt", generatedApiImports.findAndRemoveFqns(topLevelCode))
val topLevelCode = code.topLevelCode ?: ""
val filename = "${code.target.name}$GENERATED_MODIFICATIONS_SUFFIX"
val generatedModificationsFile = psiFactory.createFile(filename, generatedApiImports.findAndRemoveFqns(topLevelCode))
generatedModificationsFile.packageFqName = apiPackageFqName
val compatibilityTopLevelDeclarations = mutableListOf<KtDeclaration>()
val declarations = generatedApiFile.declarations
val declarations = generatedModificationsFile.declarations
for (declaration in declarations) {
if (declaration.firstChild.text.contains("Deprecated")) {
compatibilityTopLevelDeclarations.add(declaration.copy() as KtDeclaration)
declaration.delete()
continue
}
}
if (compatibilityTopLevelDeclarations.isNotEmpty()) {
topLevelDeclarations.putValue(apiFile, apiClass to compatibilityTopLevelDeclarations)
}
val visibility = apiClass.visibilityModifierType().takeIf { !apiClass.isPublic }
if (visibility != null) {
generatedApiFile.declarations.forEach { it.addModifier(visibility) }
generatedModificationsFile.declarations.forEach { it.addModifier(visibility) }
}
val apiTargetDirectory = targetDirectory.parent!!
apiTargetDirectory.findFile(generatedApiFile.name)?.delete()
apiTargetDirectory.findFile(generatedModificationsFile.name)?.delete()
//todo remove other old generated files
val addedFile = apiTargetDirectory.add(generatedApiFile) as KtFile
val addedFile = apiTargetDirectory.add(generatedModificationsFile) as KtFile
generatedFiles.add(addedFile)
importsByFile[addedFile] = generatedApiImports
}
val implementationClassText = code.implementationClass
if (implementationClassText != null) {
val implPackageFqnName = "${apiFile.packageFqName.asString()}.impl"
val implPackageFqnName = "${apiPackageFqName.asString()}.impl"
val implImports = Imports(implPackageFqnName)
val implFile = psiFactory.createFile("${code.target.name}Impl.kt", implImports.findAndRemoveFqns(implementationClassText))
val implFile =
psiFactory.createFile("${code.target.name}$GENERATED_IMPL_SUFFIX", implImports.findAndRemoveFqns(implementationClassText))
apiClass.containingKtFile.importDirectives.mapNotNull { it.importPath }.forEach { import ->
implImports.add(import.pathStr)
}
targetDirectory.findFile(implFile.name)?.delete()
//todo remove other old generated files
targetDirectory.findFile(implFile.name)?.delete() //todo remove other old generated files
val addedFile = targetDirectory.add(implFile) as KtFile
generatedFiles.add(addedFile)
importsByFile[addedFile] = implImports
@@ -437,13 +404,13 @@ object CodeWriter {
}
private fun copyHeaderComment(apiFile: KtFile, implFile: KtFile) {
val apiPackageDirectiveNode = apiFile.packageDirective?.node ?: return
val fileNode = implFile.node
var nodeToCopy = apiPackageDirectiveNode.treePrev
var anchorBefore = fileNode.firstChildNode
val firstNonComment = apiFile.fileAnnotationList?.node ?: apiFile.packageDirective?.node ?: return
val implFileNode = implFile.node
var nodeToCopy = firstNonComment.treePrev
var anchorBefore = implFileNode.firstChildNode
while (nodeToCopy != null) {
val copied = nodeToCopy.copyElement()
fileNode.addChild(copied, anchorBefore)
implFileNode.addChild(copied, anchorBefore)
anchorBefore = copied
nodeToCopy = nodeToCopy.treePrev
}
@@ -466,82 +433,21 @@ object CodeWriter {
}
}
private fun addInnerDeclarations(ktClass: KtClassOrObject, code: ObjClassGeneratedCode, imports: Imports, savedCompatibilityInvokeCode: MutableMap<KtClassOrObject, PsiElement>) {
if (code.builderInterface.isEmpty()) return
val psiFactory = KtPsiFactory(ktClass.project)
val builderInterface = ktClass.addDeclaration(psiFactory.createClass(imports.findAndRemoveFqns(code.builderInterface)))
val companionObject = ktClass.addDeclaration(psiFactory.createObject(imports.findAndRemoveFqns(code.companionObject)))
val body = ktClass.getOrCreateBody()
addGeneratedRegionStartComment(body, builderInterface)
addGeneratedRegionEndComment(body, companionObject)
val compatibilityInvoke = savedCompatibilityInvokeCode.remove(ktClass) ?: return
addCompatibilityInvoke(companionObject, compatibilityInvoke, psiFactory)
}
private fun addCompatibilityInvoke(companionObject: KtObjectDeclaration, compatibilityInvoke: PsiElement, psiFactory: KtPsiFactory) {
val companionBody = companionObject.getOrCreateBody()
companionBody.addBefore(compatibilityInvoke, companionBody.rBrace)
companionBody.addBefore(psiFactory.createNewLine(), companionBody.rBrace)
companionBody.addBefore(psiFactory.createComment(GENERATED_COMPATIBILITY_REGION_END), companionBody.rBrace)
}
private fun addGeneratedRegionStartComment(parent: KtElement, place: KtElement) {
val psiFactory = KtPsiFactory(parent.project)
val startCommentElements = psiFactory.createFile("\n$GENERATED_REGION_START\n").children
parent.addRangeBefore(startCommentElements.first { it is PsiWhiteSpace }, startCommentElements.last(), place)
}
private fun addGeneratedRegionEndComment(parent: KtElement, place: KtElement) {
val psiFactory = KtPsiFactory(parent.project)
val endCommentElements = psiFactory.createFile("\n$GENERATED_REGION_END\n").children
parent.addRangeAfter(endCommentElements.first { it is PsiWhiteSpace }, endCommentElements.last(), place)
}
private fun removeChildrenInGeneratedRegions(node: ASTNode) {
for (region in findGeneratedRegions(node)) {
CodeEditUtil.removeChildren(node, region.first, region.second)
private fun getSourceClassNameForGeneratedFile(file: KtFile): String? {
val packageName = file.packageFqName.asString()
if (file.name.endsWith(GENERATED_IMPL_SUFFIX)) {
// packageName includes "impl"
return "${packageName.dropLast(4)}${file.name.dropLast(GENERATED_IMPL_SUFFIX.length)}"
}
if (file.name.endsWith(GENERATED_MODIFICATIONS_SUFFIX)) {
return "$packageName.${file.name.dropLast(GENERATED_MODIFICATIONS_SUFFIX.length)}"
}
return null
}
private fun findGeneratedRegions(node: ASTNode): ArrayList<Pair<ASTNode, ASTNode>> {
val generatedRegions = ArrayList<Pair<ASTNode, ASTNode>>()
var regionStart: ASTNode? = null
node.children().forEach { child ->
if (child.isGeneratedRegionStart) {
regionStart = if (child.treePrev?.elementType == KtTokens.WHITE_SPACE) child.treePrev else child
}
else if (child.isGeneratedRegionEnd) {
regionStart?.let { generatedRegions.add(it to child) }
}
}
return generatedRegions
}
private const val GENERATED_IMPL_SUFFIX = "Impl.kt"
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)}"
}
// generated region
private const val GENERATED_REGION_START = "//region generated code"
private const val GENERATED_REGION_END = "//endregion"
private val ASTNode.isGeneratedRegionStart: Boolean
get() =
elementType == KtTokens.EOL_COMMENT && text == GENERATED_REGION_START || firstChildNode?.isGeneratedRegionStart == true
private val ASTNode.isGeneratedRegionEnd: Boolean
get() =
elementType == KtTokens.EOL_COMMENT && text == GENERATED_REGION_END
// compatability region
private const val GENERATED_COMPATIBILITY_REGION_START = "//region compatibility generated code"
private const val GENERATED_COMPATIBILITY_REGION_END = "//endregion compatibility generated code"
private const val GENERATED_MODIFICATIONS_SUFFIX = "Modifications.kt"
private const val GENERATED_METADATA_STORAGE_FILE = "MetadataStorageImpl.kt"
@@ -52,6 +52,7 @@ jvm_library(
"//plugins/editorconfig/backend",
"//plugins/editorconfig/backend:backend_test_lib",
"//plugins/editorconfig:editorconfig-plugin",
"@lib//:studio-platform",
],
runtime_deps = ["//plugins/devkit"]
)
@@ -41,5 +41,6 @@
<orderEntry type="library" scope="TEST" name="kotlinc.kotlin-scripting-common" level="project" />
<orderEntry type="module" module-name="intellij.editorconfig.backend" scope="TEST" />
<orderEntry type="module" module-name="intellij.editorconfig.plugin" />
<orderEntry type="library" scope="TEST" name="studio-platform" level="project" />
</component>
</module>
@@ -1,8 +0,0 @@
//some copyright comment
package com.intellij.workspaceModel.test.api
import com.intellij.platform.workspace.storage.WorkspaceEntity
interface SimpleEntity : WorkspaceEntity {
val name: String
}
@@ -1,44 +0,0 @@
@file:JvmName("SimpleEntityModifications")
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.WorkspaceEntityBuilder
@GeneratedCodeApiVersion(3)
interface SimpleEntityBuilder: WorkspaceEntityBuilder<SimpleEntity>{
override var entitySource: EntitySource
var name: String
}
internal object SimpleEntityType : EntityType<SimpleEntity, SimpleEntityBuilder>(){
override val entityClass: Class<SimpleEntity> get() = SimpleEntity::class.java
operator fun invoke(
name: String,
entitySource: EntitySource,
init: (SimpleEntityBuilder.() -> Unit)? = null,
): SimpleEntityBuilder{
val builder = builder()
builder.name = name
builder.entitySource = entitySource
init?.invoke(builder)
return builder
}
}
fun MutableEntityStorage.modifySimpleEntity(
entity: SimpleEntity,
modification: SimpleEntityBuilder.() -> Unit,
): SimpleEntity = modifyEntity(SimpleEntityBuilder::class.java, entity, modification)
@JvmOverloads
@JvmName("createSimpleEntity")
fun SimpleEntity(
name: String,
entitySource: EntitySource,
init: (SimpleEntityBuilder.() -> Unit)? = null,
): SimpleEntityBuilder = SimpleEntityType(name, entitySource, init)
@@ -1,26 +0,0 @@
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.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")
var typeMetadata: StorageTypeMetadata
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 = "name", valueType = primitiveTypeStringNotNullable, withDefault = false)), extProperties = listOf(), isAbstract = false)
addMetadata(typeMetadata)
}
override fun initializeMetadataHash(){
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SimpleEntity", metadataHash = -455948645)
}
}
@@ -1,183 +0,0 @@
//some copyright comment
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.WorkspaceEntityBuilder
import com.intellij.platform.workspace.storage.WorkspaceEntityInternalApi
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.SimpleEntityBuilder
@GeneratedCodeApiVersion(3)
@GeneratedCodeImplVersion(7)
@OptIn(WorkspaceEntityInternalApi::class)
internal class SimpleEntityImpl(private val dataSource: SimpleEntityData): SimpleEntity, WorkspaceEntityBase(dataSource) {
private companion object {
private val connections = listOf<ConnectionId>()
}
override val name: String
get() {
readField("name")
return dataSource.name
}
override val entitySource: EntitySource
get() {
readField("entitySource")
return dataSource.entitySource
}
override fun connectionIdList(): List<ConnectionId> {
return connections
}
internal class Builder(result: SimpleEntityData?): ModifiableWorkspaceEntityBase<SimpleEntity, SimpleEntityData>(result), SimpleEntityBuilder {
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<ConnectionId>{
return connections
}
// Relabeling code, move information from dataSource to this builder
override fun relabel(dataSource: WorkspaceEntity, parents: Set<WorkspaceEntity>?){
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> = SimpleEntity::class.java
}
}
@OptIn(WorkspaceEntityInternalApi::class)
internal class SimpleEntityData : WorkspaceEntityData<SimpleEntity>(){
lateinit var name: String
internal fun isNameInitialized(): Boolean = ::name.isInitialized
override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntityBuilder<SimpleEntity>{
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<out WorkspaceEntity>{
return SimpleEntity::class.java
}
override fun createDetachedEntity(parents: List<WorkspaceEntityBuilder<*>>): WorkspaceEntityBuilder<*>{
return SimpleEntity(name, entitySource)
}
override fun getRequiredParents(): List<Class<out WorkspaceEntity>>{
val res = mutableListOf<Class<out WorkspaceEntity>>()
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
}
}
@@ -1,8 +0,0 @@
//some copyright comment
package com.intellij.workspaceModel.test.api
import com.intellij.platform.workspace.storage.WorkspaceEntity
interface SimpleEntity : WorkspaceEntity {
val name: String
}
@@ -1,7 +1,5 @@
package com.intellij.workspaceModel.test.api
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
@@ -1,7 +1,7 @@
package com.intellij.workspaceModel.test.api
import com.intellij.platform.workspace.storage.WorkspaceEntity
import com.intellij.platform.workspace.storage.annotations.Default
import com.intellij.platform.workspace.storage.WorkspaceEntity
interface DefaultFieldEntity : WorkspaceEntity {
val version: Int
@@ -1,9 +1,9 @@
package com.intellij.workspaceModel.test.api
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.GeneratedCodeApiVersion
import com.intellij.platform.workspace.storage.WorkspaceEntityBuilder
import com.intellij.platform.workspace.storage.MutableEntityStorage
interface FinalFieldsEntity : WorkspaceEntity {
@@ -0,0 +1,3 @@
package com.intellij.another.module
class ClassToImport
@@ -0,0 +1,18 @@
// 2700-2200 BCE fake copyright for test
// another line of fake copyright
package com.intellij.workspaceModel.test.api
// imports are in wrong order purposefully
import java.nio.file.Path
import com.intellij.platform.workspace.storage.WorkspaceEntity
import java.io.File
import com.intellij.another.module.ClassToImport
interface SimpleEntity : WorkspaceEntity {
val version: Int
val name: String
val isSimple: Boolean
val imported: ClassToImport
}
data class UnrelatedToWsm(val name: String, val file: File, val path: Path)
@@ -1,12 +1,14 @@
// 2700-2200 BCE fake copyright for test
// another line of fake copyright
@file:JvmName("SimpleEntityModifications")
package com.intellij.workspaceModel.test.api
import com.intellij.another.module.ClassToImport
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.WorkspaceEntityBuilder
@GeneratedCodeApiVersion(3)
@@ -15,6 +17,7 @@ interface SimpleEntityBuilder : WorkspaceEntityBuilder<SimpleEntity> {
var version: Int
var name: String
var isSimple: Boolean
var imported: ClassToImport
}
internal object SimpleEntityType : EntityType<SimpleEntity, SimpleEntityBuilder>() {
@@ -23,6 +26,7 @@ internal object SimpleEntityType : EntityType<SimpleEntity, SimpleEntityBuilder>
version: Int,
name: String,
isSimple: Boolean,
imported: ClassToImport,
entitySource: EntitySource,
init: (SimpleEntityBuilder.() -> Unit)? = null,
): SimpleEntityBuilder {
@@ -30,6 +34,7 @@ internal object SimpleEntityType : EntityType<SimpleEntity, SimpleEntityBuilder>
builder.version = version
builder.name = name
builder.isSimple = isSimple
builder.imported = imported
builder.entitySource = entitySource
init?.invoke(builder)
return builder
@@ -47,6 +52,7 @@ fun SimpleEntity(
version: Int,
name: String,
isSimple: Boolean,
imported: ClassToImport,
entitySource: EntitySource,
init: (SimpleEntityBuilder.() -> Unit)? = null,
): SimpleEntityBuilder = SimpleEntityType(version, name, isSimple, entitySource, init)
): SimpleEntityBuilder = SimpleEntityType(version, name, isSimple, imported, entitySource, init)
@@ -1,3 +1,5 @@
// 2700-2200 BCE fake copyright for test
// another line of fake copyright
package com.intellij.workspaceModel.test.api.impl
import com.intellij.platform.workspace.storage.WorkspaceEntityInternalApi
@@ -45,6 +47,16 @@ internal object MetadataStorageImpl : MetadataStorageBase() {
isOpen = false,
name = "isSimple",
valueType = primitiveTypeBooleanNotNullable,
withDefault = false),
OwnPropertyMetadata(isComputable = false,
isKey = false,
isOpen = false,
name = "imported",
valueType = ValueTypeMetadata.SimpleType.CustomType(isNullable = false,
typeMetadata = FinalClassMetadata.ClassMetadata(
fqName = "com.intellij.another.module.ClassToImport",
properties = listOf(),
supertypes = listOf())),
withDefault = false)),
extProperties = listOf(),
isAbstract = false)
@@ -53,6 +65,7 @@ internal object MetadataStorageImpl : MetadataStorageBase() {
}
override fun initializeMetadataHash() {
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SimpleEntity", metadataHash = 284369588)
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SimpleEntity", metadataHash = 711104309)
addMetadataHash(typeFqn = "com.intellij.another.module.ClassToImport", metadataHash = -2116758707)
}
}
@@ -1,5 +1,8 @@
// 2700-2200 BCE fake copyright for test
// another line of fake copyright
package com.intellij.workspaceModel.test.api.impl
import com.intellij.another.module.ClassToImport
import com.intellij.platform.workspace.storage.ConnectionId
import com.intellij.platform.workspace.storage.EntitySource
import com.intellij.platform.workspace.storage.GeneratedCodeApiVersion
@@ -43,6 +46,11 @@ internal class SimpleEntityImpl(private val dataSource: SimpleEntityData) : Simp
readField("isSimple")
return dataSource.isSimple
}
override val imported: ClassToImport
get() {
readField("imported")
return dataSource.imported
}
override val entitySource: EntitySource
get() {
@@ -88,6 +96,9 @@ internal class SimpleEntityImpl(private val dataSource: SimpleEntityData) : Simp
if (!getEntityData().isNameInitialized()) {
error("Field SimpleEntity#name should be initialized")
}
if (!getEntityData().isImportedInitialized()) {
error("Field SimpleEntity#imported should be initialized")
}
}
override fun connectionIdList(): List<ConnectionId> {
@@ -101,6 +112,7 @@ internal class SimpleEntityImpl(private val dataSource: SimpleEntityData) : Simp
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
if (this.imported != dataSource.imported) this.imported = dataSource.imported
updateChildToParentReferences(parents)
}
@@ -134,6 +146,14 @@ internal class SimpleEntityImpl(private val dataSource: SimpleEntityData) : Simp
getEntityData(true).isSimple = value
changedProperty.add("isSimple")
}
override var imported: ClassToImport
get() = getEntityData().imported
set(value) {
checkModificationAllowed()
getEntityData(true).imported = value
changedProperty.add("imported")
}
override fun getEntityClass(): Class<SimpleEntity> = SimpleEntity::class.java
}
@@ -145,10 +165,12 @@ internal class SimpleEntityData : WorkspaceEntityData<SimpleEntity>() {
var version: Int = 0
lateinit var name: String
var isSimple: Boolean = false
lateinit var imported: ClassToImport
internal fun isNameInitialized(): Boolean = ::name.isInitialized
internal fun isImportedInitialized(): Boolean = ::imported.isInitialized
override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntityBuilder<SimpleEntity> {
val modifiable = SimpleEntityImpl.Builder(null)
@@ -177,7 +199,7 @@ internal class SimpleEntityData : WorkspaceEntityData<SimpleEntity>() {
}
override fun createDetachedEntity(parents: List<WorkspaceEntityBuilder<*>>): WorkspaceEntityBuilder<*> {
return SimpleEntity(version, name, isSimple, entitySource)
return SimpleEntity(version, name, isSimple, imported, entitySource)
}
override fun getRequiredParents(): List<Class<out WorkspaceEntity>> {
@@ -193,6 +215,7 @@ internal class SimpleEntityData : WorkspaceEntityData<SimpleEntity>() {
if (this.version != other.version) return false
if (this.name != other.name) return false
if (this.isSimple != other.isSimple) return false
if (this.imported != other.imported) return false
return true
}
@@ -203,6 +226,7 @@ internal class SimpleEntityData : WorkspaceEntityData<SimpleEntity>() {
if (this.version != other.version) return false
if (this.name != other.name) return false
if (this.isSimple != other.isSimple) return false
if (this.imported != other.imported) return false
return true
}
@@ -211,6 +235,7 @@ internal class SimpleEntityData : WorkspaceEntityData<SimpleEntity>() {
result = 31 * result + version.hashCode()
result = 31 * result + name.hashCode()
result = 31 * result + isSimple.hashCode()
result = 31 * result + imported.hashCode()
return result
}
@@ -219,6 +244,7 @@ internal class SimpleEntityData : WorkspaceEntityData<SimpleEntity>() {
result = 31 * result + version.hashCode()
result = 31 * result + name.hashCode()
result = 31 * result + isSimple.hashCode()
result = 31 * result + imported.hashCode()
return result
}
}
@@ -0,0 +1,3 @@
package com.intellij.another.module
class ClassToImport
@@ -0,0 +1,18 @@
// 2700-2200 BCE fake copyright for test
// another line of fake copyright
package com.intellij.workspaceModel.test.api
// imports are in wrong order purposefully
import java.nio.file.Path
import com.intellij.platform.workspace.storage.WorkspaceEntity
import java.io.File
import com.intellij.another.module.ClassToImport
interface SimpleEntity : WorkspaceEntity {
val version: Int
val name: String
val isSimple: Boolean
val imported: ClassToImport
}
data class UnrelatedToWsm(val name: String, val file: File, val path: Path)
@@ -14,10 +14,6 @@ interface ParentEntity : GrandParentEntity {
val data2: String
}
//region generated code
fun MutableEntityStorage.modifyEntity(entity: ParentEntity, modification: ParentEntity.Builder.() -> Unit) = oldCode
//endregion
interface ChildEntity: ParentEntity {
val data3: String
}
@@ -1,32 +0,0 @@
// 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.Parent
import com.intellij.platform.workspace.storage.url.VirtualFileUrl
import java.net.URL
interface EntityWithManyImports : WorkspaceEntityWithSymbolicId {
val version: Int
val name: String
val files: List<SimpleEntity>
val pointer: EntityPointer<SimpleEntity>
override val symbolicId: SimpleId
get() = SimpleId(name)
}
data class SimpleId(val name: String) : SymbolicEntityId<EntityWithManyImports> {
override val presentableName: String
get() = name
}
interface SimpleEntity : WorkspaceEntity {
val url: VirtualFileUrl
@Parent val parent: EntityWithManyImports
}
data class UnrelatedToEntities(val name: String, val data: EntityPointer<SimpleEntity>) {
fun doSomething(src: EntitySource) {
}
}
@@ -1,51 +0,0 @@
@file:JvmName("EntityWithManyImportsModifications")
package com.intellij.workspaceModel.test.api
import com.intellij.platform.workspace.storage.*
import com.intellij.platform.workspace.storage.annotations.Parent
import com.intellij.platform.workspace.storage.url.VirtualFileUrl
import java.net.URL
@GeneratedCodeApiVersion(3)
interface EntityWithManyImportsBuilder: WorkspaceEntityBuilder<EntityWithManyImports>{
override var entitySource: EntitySource
var version: Int
var name: String
var files: List<SimpleEntityBuilder>
var pointer: EntityPointer
}
internal object EntityWithManyImportsType : EntityType<EntityWithManyImports, EntityWithManyImportsBuilder>(){
override val entityClass: Class<EntityWithManyImports> get() = EntityWithManyImports::class.java
operator fun invoke(
version: Int,
name: String,
pointer: EntityPointer,
entitySource: EntitySource,
init: (EntityWithManyImportsBuilder.() -> Unit)? = null,
): EntityWithManyImportsBuilder{
val builder = builder()
builder.version = version
builder.name = name
builder.pointer = pointer
builder.entitySource = entitySource
init?.invoke(builder)
return builder
}
}
fun MutableEntityStorage.modifyEntityWithManyImports(
entity: EntityWithManyImports,
modification: EntityWithManyImportsBuilder.() -> Unit,
): EntityWithManyImports = modifyEntity(EntityWithManyImportsBuilder::class.java, entity, modification)
@JvmOverloads
@JvmName("createEntityWithManyImports")
fun EntityWithManyImports(
version: Int,
name: String,
pointer: EntityPointer,
entitySource: EntitySource,
init: (EntityWithManyImportsBuilder.() -> Unit)? = null,
): EntityWithManyImportsBuilder = EntityWithManyImportsType(version, name, pointer, entitySource, init)
@@ -1,43 +0,0 @@
@file:JvmName("SimpleEntityModifications")
package com.intellij.workspaceModel.test.api
import com.intellij.platform.workspace.storage.*
import com.intellij.platform.workspace.storage.annotations.Parent
import com.intellij.platform.workspace.storage.url.VirtualFileUrl
import java.net.URL
@GeneratedCodeApiVersion(3)
interface SimpleEntityBuilder: WorkspaceEntityBuilder<SimpleEntity>{
override var entitySource: EntitySource
var url: VirtualFileUrl
var parent: EntityWithManyImportsBuilder
}
internal object SimpleEntityType : EntityType<SimpleEntity, SimpleEntityBuilder>(){
override val entityClass: Class<SimpleEntity> get() = SimpleEntity::class.java
operator fun invoke(
url: VirtualFileUrl,
entitySource: EntitySource,
init: (SimpleEntityBuilder.() -> Unit)? = null,
): SimpleEntityBuilder{
val builder = builder()
builder.url = url
builder.entitySource = entitySource
init?.invoke(builder)
return builder
}
}
fun MutableEntityStorage.modifySimpleEntity(
entity: SimpleEntity,
modification: SimpleEntityBuilder.() -> Unit,
): SimpleEntity = modifyEntity(SimpleEntityBuilder::class.java, entity, modification)
@JvmOverloads
@JvmName("createSimpleEntity")
fun SimpleEntity(
url: VirtualFileUrl,
entitySource: EntitySource,
init: (SimpleEntityBuilder.() -> Unit)? = null,
): SimpleEntityBuilder = SimpleEntityType(url, entitySource, init)
@@ -1,284 +0,0 @@
// 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.Parent
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.EntityWithManyImportsBuilder
import com.intellij.workspaceModel.test.api.SimpleEntity
import com.intellij.workspaceModel.test.api.SimpleEntityBuilder
import com.intellij.workspaceModel.test.api.SimpleId
import java.net.URL
@GeneratedCodeApiVersion(3)
@GeneratedCodeImplVersion(7)
@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<ConnectionId>(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<SimpleEntity>
get() = snapshot.extractOneToManyChildren<SimpleEntity>(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<ConnectionId> {
return connections
}
internal class Builder(result: EntityWithManyImportsData?): ModifiableWorkspaceEntityBase<EntityWithManyImports, EntityWithManyImportsData>(result), EntityWithManyImportsBuilder {
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<WorkspaceEntityBase>(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<ConnectionId>{
return connections
}
// Relabeling code, move information from dataSource to this builder
override fun relabel(dataSource: WorkspaceEntity, parents: Set<WorkspaceEntity>?){
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<SimpleEntity>? = emptyList()
override var files: List<SimpleEntityBuilder>
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<SimpleEntityBuilder>) + (this.entityLinks[EntityLink(true, FILES_CONNECTION_ID)] as? List<SimpleEntityBuilder> ?: emptyList())
} else {
this.entityLinks[EntityLink(true, FILES_CONNECTION_ID)] as? List<SimpleEntityBuilder> ?: 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<WorkspaceEntity, *>)
}
}
_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> = EntityWithManyImports::class.java
}
}
@OptIn(WorkspaceEntityInternalApi::class)
internal class EntityWithManyImportsData : WorkspaceEntityData<EntityWithManyImports>(){
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): WorkspaceEntityBuilder<EntityWithManyImports>{
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<out WorkspaceEntity>{
return EntityWithManyImports::class.java
}
override fun createDetachedEntity(parents: List<WorkspaceEntityBuilder<*>>): WorkspaceEntityBuilder<*>{
return EntityWithManyImports(version, name, pointer, entitySource)
}
override fun getRequiredParents(): List<Class<out WorkspaceEntity>>{
val res = mutableListOf<Class<out WorkspaceEntity>>()
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
}
}
@@ -1,39 +0,0 @@
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 = -201548353)
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SimpleEntity", metadataHash = -657222679)
addMetadataHash(typeFqn = "com.intellij.workspaceModel.test.api.SimpleId", metadataHash = 2085950989)
addMetadataHash(typeFqn = "com.intellij.platform.workspace.storage.SymbolicEntityId", metadataHash = 1931133873)
}
}
@@ -1,239 +0,0 @@
// 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.Parent
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.EntityWithManyImportsBuilder
import com.intellij.workspaceModel.test.api.SimpleEntity
import com.intellij.workspaceModel.test.api.SimpleEntityBuilder
import java.net.URL
@GeneratedCodeApiVersion(3)
@GeneratedCodeImplVersion(7)
@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<ConnectionId>(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<ConnectionId> {
return connections
}
internal class Builder(result: SimpleEntityData?): ModifiableWorkspaceEntityBase<SimpleEntity, SimpleEntityData>(result), SimpleEntityBuilder {
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<WorkspaceEntityBase>(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<ConnectionId>{
return connections
}
// Relabeling code, move information from dataSource to this builder
override fun relabel(dataSource: WorkspaceEntity, parents: Set<WorkspaceEntity>?){
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: EntityWithManyImportsBuilder
get(){
val _diff = diff
return if (_diff != null) {
@OptIn(EntityStorageInstrumentationApi::class)
((_diff as MutableEntityStorageInstrumentation).getParentBuilder(PARENT_CONNECTION_ID, this) as? EntityWithManyImportsBuilder) ?: (this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)]!! as EntityWithManyImportsBuilder)
} else {
this.entityLinks[EntityLink(false, PARENT_CONNECTION_ID)]!! as EntityWithManyImportsBuilder
}
}
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<Any> ?: 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<WorkspaceEntity, *>)
}
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<Any> ?: 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> = SimpleEntity::class.java
}
}
@OptIn(WorkspaceEntityInternalApi::class)
internal class SimpleEntityData : WorkspaceEntityData<SimpleEntity>(){
lateinit var url: VirtualFileUrl
internal fun isUrlInitialized(): Boolean = ::url.isInitialized
override fun wrapAsModifiable(diff: MutableEntityStorage): WorkspaceEntityBuilder<SimpleEntity>{
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<out WorkspaceEntity>{
return SimpleEntity::class.java
}
override fun createDetachedEntity(parents: List<WorkspaceEntityBuilder<*>>): WorkspaceEntityBuilder<*>{
return SimpleEntity(url, entitySource){
parents.filterIsInstance<EntityWithManyImportsBuilder>().singleOrNull()?.let { this.parent = it }
}
}
override fun getRequiredParents(): List<Class<out WorkspaceEntity>>{
val res = mutableListOf<Class<out WorkspaceEntity>>()
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
}
}
@@ -1,33 +0,0 @@
// 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.Parent
import com.intellij.platform.workspace.storage.url.VirtualFileUrl
interface EntityWithManyImports : WorkspaceEntityWithSymbolicId {
val version: Int
val name: String
val files: List<SimpleEntity>
val pointer: EntityPointer<SimpleEntity>
override val symbolicId: SimpleId
get() = SimpleId(name)
}
data class SimpleId(val name: String) : SymbolicEntityId<EntityWithManyImports> {
override val presentableName: String
get() = name
}
interface SimpleEntity : WorkspaceEntity {
val url: VirtualFileUrl
@Parent val parent: EntityWithManyImports
}
data class UnrelatedToEntities(val name: String, val data: EntityPointer<SimpleEntity>) {
fun doSomething(src: EntitySource) {
}
}
@@ -1,4 +1,3 @@
// 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.*
@@ -1,4 +1,3 @@
// 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.*
@@ -1,4 +1,3 @@
// 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.*
@@ -1,4 +1,3 @@
// 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.*
@@ -1,7 +1,7 @@
package com.intellij.workspaceModel.test.api
import com.intellij.platform.workspace.jps.entities.ContentRootEntity
import com.intellij.platform.workspace.storage.WorkspaceEntity
import com.intellij.platform.workspace.jps.entities.ContentRootEntity
import com.intellij.platform.workspace.storage.annotations.Parent
interface ReferredEntity : WorkspaceEntity {
@@ -1,9 +0,0 @@
package com.intellij.workspaceModel.test.api
import com.intellij.platform.workspace.storage.WorkspaceEntity
interface SimpleEntity : WorkspaceEntity {
val version: Int
val name: String
val isSimple: Boolean
}
@@ -1,9 +0,0 @@
package com.intellij.workspaceModel.test.api
import com.intellij.platform.workspace.storage.WorkspaceEntity
interface SimpleEntity : WorkspaceEntity {
val version: Int
val name: String
val isSimple: Boolean
}
@@ -7,4 +7,25 @@ interface SimpleEntity : WorkspaceEntity {
val version: Int
val name: String
val isSimple: Boolean
}
//region generated code
@GeneratedCodeApiVersion(1)
interface Builder : SimpleEntity, WorkspaceEntityBuilder<SimpleEntity> {
//obsolete
}
companion object : Type<SimpleEntity, Builder>() {
operator fun invoke(version: Int,
entitySource: EntitySource,
name: String,
isSimple: Boolean,
init: (Builder.() -> Unit)? = null): SimpleEntity {
TODO("obsolete")
}
}
//endregion
}
//region generated code
fun MutableEntityStorage.oldCode() = Unit
//endregion
@@ -1,4 +1,3 @@
//new comment
package com.intellij.workspaceModel.test.api.impl
import com.intellij.platform.workspace.storage.ConnectionId
@@ -96,7 +96,7 @@ abstract class AbstractAllIntellijEntitiesGenerationTest : CodeGenerationTestBas
var storageChanged = false
modulesToCheck.forEachIndexed { index, (moduleEntity, sourceRoot) ->
println("[${index + 1}/${modulesToCheck.size}] Generating workspace code for module ${moduleEntity.name}")
println("[${index + 1}/${modulesToCheck.size}] Generating workspace code for module ${moduleEntity.name} [${sourceRoot.url.presentableUrl}]")
val isTestModule = sourceRoot.rootTypeId == JAVA_TEST_ROOT_ENTITY_TYPE_ID
val libraries = LibrariesRequiredForWorkspace.getRelatedLibraries(moduleEntity.name)
val gen = generateWorkspaceCode(moduleEntity, sourceRoot, isTestModule, libraries)
@@ -233,7 +233,7 @@ abstract class AbstractAllIntellijEntitiesGenerationTest : CodeGenerationTestBas
}
return null
}
private fun createGenSourceRoot(storage: MutableEntityStorage, sourceRoot: SourceRootEntity): SourceRootEntity {
val genFolderVirtualFile = VfsUtil.createDirectories("${sourceRoot.contentRoot.url.presentableUrl}/${WorkspaceModelGenerator.GENERATED_FOLDER_NAME}")
val javaSourceRoot = sourceRoot.javaSourceRoots.first()
@@ -324,9 +324,9 @@ abstract class AbstractAllIntellijEntitiesGenerationTest : CodeGenerationTestBas
srcRoots@ for (sourceRoot in storage.entities<SourceRootEntity>()) {
val moduleEntity = sourceRoot.contentRoot.module
var toCheck = false
if (moduleEntity.name in skippedModules) continue
if (sourceRoot.javaSourceRoots.none { !it.generated }) continue
var toCheck = false
for (file in File(sourceRoot.url.presentableUrl).walk()) {
if (file.isFile && file.extension == "kt") {
@@ -19,7 +19,10 @@ abstract class AbstractEntityCodeGenTest : CodeGenerationTestBase() {
doTest()
}
fun testSimpleCaseWithCodestyle() {
/**
* Tests that code is formatted properly: codestyle, copyright is added, imports are optimized.
*/
fun testFormat() {
doTest(formatCode = true)
}
@@ -79,14 +82,6 @@ abstract class AbstractEntityCodeGenTest : CodeGenerationTestBase() {
doTest(processAbstractTypes = true)
}
fun testAddCopyrightComment() {
doTest(processAbstractTypes = true)
}
fun testImports() {
doTest()
}
fun testOpenClassProperty() {
doTest(processAbstractTypes = true)
}
@@ -1,6 +1,7 @@
// 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
import com.android.ide.common.repository.AgpVersion
import com.intellij.openapi.application.ArchivedCompilationContextUtil
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.openapi.roots.OrderRootType
@@ -21,7 +22,8 @@ internal object LibrariesRequiredForWorkspace {
private val rider = ModuleLibrary("intellij.rider")
private val riderUnityPlugin = ModuleLibrary("intellij.rider.plugins.unity")
private val riderModel = ModuleLibrary("intellij.rider.model.generated")
private val riderModelGenerated = ModuleLibrary("intellij.rider.model.generated")
private val rdIdeModelGenerated = ModuleLibrary("intellij.rd.ide.model.generated")
private val riderRdClient = ModuleLibrary("intellij.rider.rdclient.dotnet")
private val gradle = ModuleLibrary("intellij.gradle")
private val gradleToolingExtension = ModuleLibrary("intellij.gradle.toolingExtension")
@@ -29,11 +31,14 @@ internal object LibrariesRequiredForWorkspace {
private val pyCommon = ModuleLibrary("intellij.python.common")
private val cidrProjectModel = ModuleLibrary("intellij.cidr.projectModel")
private val kotlinBaseScripting = ModuleLibrary("intellij.kotlin.base.scripting")
private val androidProjectSystem = ModuleLibrary("intellij.android.projectSystem")
private val androidGradleModels = ModuleLibrary("intellij.android.projectSystem.gradle.models")
private val kotlinJpsCommon = JarLibrary("kotlinc-kotlin-jps-common", KotlinModuleKind::class.java)
private val kotlinScriptingCommon = JarLibrary("kotlinc-kotlin-scripting-common", SourceCode::class.java)
private val rdCore = JarLibrary("rd-core", IPrintable::class.java)
private val rdFramework = JarLibrary("rd-framework", RdId::class.java)
private val androidStudioPlatform = JarLibrary("studio-platform", AgpVersion::class.java)
fun getRelatedLibraries(moduleEntityName: String): List<RelatedLibrary> =
when (moduleEntityName) {
@@ -47,7 +52,7 @@ internal object LibrariesRequiredForWorkspace {
listOf(riderRdClient)
}
"intellij.rider.rdclient.dotnet" -> {
listOf(rdFramework, rdCore, riderModel, riderUnityPlugin, rider, riderRdClient)
listOf(rdFramework, rdCore, riderModelGenerated, riderUnityPlugin, rider, rdIdeModelGenerated)
}
"intellij.kotlin.base.facet" -> {
listOf(intellijJava, kotlinJpsCommon)
@@ -70,6 +75,9 @@ internal object LibrariesRequiredForWorkspace {
"intellij.clion.openfolder" -> {
listOf(cidrProjectModel)
}
"intellij.android.projectSystem.gradle" -> {
listOf(androidProjectSystem, androidGradleModels, androidStudioPlatform)
}
else -> {
emptyList()
}