diff --git a/platform/lang-impl/src/com/intellij/workspaceModel/ide/impl/legacyBridge/facet/ModifiableFacetModelBridgeImpl.kt b/platform/lang-impl/src/com/intellij/workspaceModel/ide/impl/legacyBridge/facet/ModifiableFacetModelBridgeImpl.kt index 20cea148f6a5..74af71f4872e 100644 --- a/platform/lang-impl/src/com/intellij/workspaceModel/ide/impl/legacyBridge/facet/ModifiableFacetModelBridgeImpl.kt +++ b/platform/lang-impl/src/com/intellij/workspaceModel/ide/impl/legacyBridge/facet/ModifiableFacetModelBridgeImpl.kt @@ -59,7 +59,7 @@ class ModifiableFacetModelBridgeImpl(private val initialStorage: EntityStorage, else -> moduleSource } if (facet is FacetBridge<*>) { - facet.addNewModuleSettings(diff, moduleEntity, source) + facet.addToStorage(diff, moduleEntity, source) } else { val facetConfigurationXml = FacetUtil.saveFacetConfiguration(facet)?.let { JDOMUtil.write(it) } val underlyingEntity = facet.underlyingFacet?.let { diff.facetMapping().getEntities(it).single() as FacetEntity } @@ -74,7 +74,7 @@ class ModifiableFacetModelBridgeImpl(private val initialStorage: EntityStorage, override fun removeFacet(facet: Facet<*>?) { if (facet == null) return if (facet is FacetBridge<*>) { - facet.removeModuleSettings(diff, moduleEntity) + facet.removeFromStorage(diff, moduleEntity) } else { val facetEntity = diff.facetMapping().getEntities(facet).singleOrNull() as? FacetEntity ?: return removeFacetEntityWithSubFacets(facetEntity) @@ -99,7 +99,7 @@ class ModifiableFacetModelBridgeImpl(private val initialStorage: EntityStorage, override fun rename(facet: Facet<*>, newName: String) { if (facet is FacetBridge<*>) { - facet.renameModuleSettings(diff, moduleEntity, newName) + facet.rename(diff, moduleEntity, newName) } else { val entity = diff.facetMapping().getEntities(facet).single() as FacetEntity val newEntity = diff.modifyEntity(entity) { @@ -113,7 +113,7 @@ class ModifiableFacetModelBridgeImpl(private val initialStorage: EntityStorage, override fun getNewName(facet: Facet<*>): String { if (facet is FacetBridge<*>) { - return facet.getNewModuleSettingsName(diff, moduleEntity) + return facet.getNewName(diff, moduleEntity) } else { val entity = diff.facetMapping().getEntities(facet).single() as FacetEntity return entity.name @@ -174,7 +174,7 @@ class ModifiableFacetModelBridgeImpl(private val initialStorage: EntityStorage, override fun isNewFacet(facet: Facet<*>): Boolean { if (facet is FacetBridge<*>) { - return facet.isNewModuleSettings(diff, moduleEntity) + return facet.isNew(diff, moduleEntity) } else { val entity = diff.facetMapping().getEntities(facet).singleOrNull() as FacetEntity? return entity != null && entity.persistentId !in initialStorage diff --git a/platform/lang-impl/src/com/intellij/workspaceModel/ide/legacyBridge/WorkspaceFacetContributor.kt b/platform/lang-impl/src/com/intellij/workspaceModel/ide/legacyBridge/WorkspaceFacetContributor.kt index 8f3b518d5a66..793624662f07 100644 --- a/platform/lang-impl/src/com/intellij/workspaceModel/ide/legacyBridge/WorkspaceFacetContributor.kt +++ b/platform/lang-impl/src/com/intellij/workspaceModel/ide/legacyBridge/WorkspaceFacetContributor.kt @@ -7,16 +7,65 @@ import com.intellij.workspaceModel.storage.WorkspaceEntity import com.intellij.workspaceModel.storage.bridgeEntities.api.ModuleEntity import org.jetbrains.annotations.ApiStatus +/** + * Originally, [com.intellij.facet.Facet] were made to support custom setting for the module, but this solution is not + * rather flexible and thanks to the workspace model we have an opportunity to make custom entities describing additional + * module settings. To have a sort of bridge between new approach with declaring custom module settings and [com.intellij.facet.Facet] + * several extension points were introduced: + * 1) [com.intellij.workspaceModel.ide.impl.jps.serialization.CustomFacetRelatedEntitySerializer] to add support custom entity + * serialization/deserialization as facet tag. + * 2) [WorkspaceFacetContributor] to have an option to fire different sorts of event related to the [com.intellij.facet.Facet] during + * the changes of your custom entity this extension point should be implemented. + * + * If you want to use your custom module setting entity under the hood of your facet you also need to implement + * [com.intellij.workspaceModel.ide.legacyBridge.FacetBridge] to be properly updated. + * + * **N.B. Most of the time you need to implement them all to have a correct support all functionality relying on Facets.** + * + * Particularly this extension point was introduced to fire all related to [com.intellij.facet.FacetManagerListener] events. + * We have a universal listener [com.intellij.workspaceModel.ide.impl.legacyBridge.facet.FacetEntityChangeListener] which base + * on changes in your entities and data calculated by this EP can fire all these events for us. + */ @ApiStatus.Internal interface WorkspaceFacetContributor { + /** + * Declare class for the main entity associated with [com.intellij.facet.Facet]. + */ val rootEntityType: Class + + /** + * Get the name for the associated with entity [com.intellij.facet.Facet] + */ fun getFacetName(entity: T): String + + /** + * Method return the module to which this entity belongs + */ fun getRelatedModuleEntity(entity: T): ModuleEntity + + /** + * Method return the entity of type declared in [rootEntityType], associated with this module if any + */ fun getRootEntityByModuleEntity(moduleEntity: ModuleEntity): T? + + /** + * Method for creating [com.intellij.facet.Facet] from the given entity of root type + */ fun createFacetFromEntity(entity: T, project: Project): Facet<*> + /** + * This field should be overridden if root entity can have children which changes can affect e.g. facet configuration + * otherwise [com.intellij.facet.FacetManagerListener.facetConfigurationChanged] will not be fired correctly. In the same time + * [getRootEntityByChild] should be implemented + */ val childEntityTypes: List> get() = emptyList() + + /** + * Method for getting entity of root type by child. + * + * **This method should be overridden only if the root entity have children([childEntityTypes] returns not null list), otherwise it shouldn't be touched.** + */ fun getRootEntityByChild(childEntity: WorkspaceEntity): T { error("Implementation of the method should be overridden because root entity contains children") } diff --git a/platform/projectModel-impl/src/com/intellij/workspaceModel/ide/legacyBridge/FacetBridge.kt b/platform/projectModel-impl/src/com/intellij/workspaceModel/ide/legacyBridge/FacetBridge.kt index c40debc47195..efb44da40b51 100644 --- a/platform/projectModel-impl/src/com/intellij/workspaceModel/ide/legacyBridge/FacetBridge.kt +++ b/platform/projectModel-impl/src/com/intellij/workspaceModel/ide/legacyBridge/FacetBridge.kt @@ -5,13 +5,50 @@ import com.intellij.workspaceModel.storage.MutableEntityStorage import com.intellij.workspaceModel.storage.WorkspaceEntity import com.intellij.workspaceModel.storage.bridgeEntities.api.ModuleEntity +/** + * Bridge interface for facet which uses custom module settings entity under the hood + */ interface FacetBridge { - fun isNewModuleSettings(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity): Boolean - fun getNewModuleSettingsName(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity): String - fun addNewModuleSettings(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity, entitySource: EntitySource) - fun removeModuleSettings(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity) - fun renameModuleSettings(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity, newName: String) + /** + * Check if it's a newly created facet bridge, and it's root entity doesn't exist in main storage + */ + fun isNew(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity): Boolean + + /** + * Returns the new name associated with given facet bridge + */ + fun getNewName(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity): String + + /** + * Add root entity which [FacetBridge] uses under the hood, into the storage + * @param mutableStorage for saving root entity and it's children in it + * @param moduleEntity corresponds to this [FacetBridge] + * @param entitySource which should be used for such entities + */ + fun addToStorage(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity, entitySource: EntitySource) + + /** + * Removes all associated with this bridge entities from the storage + */ + fun removeFromStorage(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity) + + /** + * Rename entity associated with this bridge + */ + fun rename(mutableStorage: MutableEntityStorage, moduleEntity: ModuleEntity, newName: String) + + /** + * Apply changes from the entity which used under the hood into the builder passed as a parameter + */ fun applyChangesToStorage(mutableStorage: MutableEntityStorage, module: ModuleBridge) + + /** + * Update facet configuration base on the data from the related entity + */ fun updateFacetConfiguration(rootEntity: T) + + /** + * Method returns the entity which is used under the hood of this [FacetBridge] + */ fun getRootEntity(): T } \ No newline at end of file diff --git a/platform/workspaceModel/jps/src/com/intellij/workspaceModel/ide/impl/jps/serialization/CustomFacetRelatedEntitySerializer.kt b/platform/workspaceModel/jps/src/com/intellij/workspaceModel/ide/impl/jps/serialization/CustomFacetRelatedEntitySerializer.kt index 5d1e0ad80468..480ea94c50c0 100644 --- a/platform/workspaceModel/jps/src/com/intellij/workspaceModel/ide/impl/jps/serialization/CustomFacetRelatedEntitySerializer.kt +++ b/platform/workspaceModel/jps/src/com/intellij/workspaceModel/ide/impl/jps/serialization/CustomFacetRelatedEntitySerializer.kt @@ -11,16 +11,55 @@ import org.jetbrains.annotations.ApiStatus import org.jetbrains.jps.model.serialization.facet.FacetState /** - * The goal of this extension point is supporting serialization/deserialization of custom entities - * related to the module and located at the same .iml file + * Originally, [com.intellij.facet.Facet] were made to support custom setting for the module, but this solution is not + * rather flexible and thanks to the workspace model we have an opportunity to make custom entities describing additional + * module settings. To have a sort of bridge between new approach with declaring custom module settings and [com.intellij.facet.Facet] + * several extension points were introduced: + * 1) [CustomFacetRelatedEntitySerializer] to add support custom entity + * serialization/deserialization as facet tag. + * 2) [com.intellij.workspaceModel.ide.legacyBridge.WorkspaceFacetContributor] to have an option to fire different sorts of event related to the [com.intellij.facet.Facet] during + * the changes of your custom entity this extension point should be implemented. + * + * If you want to use your custom module setting entity under the hood of your facet you also need to implement + * [com.intellij.workspaceModel.ide.legacyBridge.FacetBridge] to be properly updated. + * + * **N.B. Most of the time you need to implement them all to have a correct support all functionality relying on Facets.** */ @ApiStatus.Internal interface CustomFacetRelatedEntitySerializer { + /** + * Declare class for the main entity associated with [com.intellij.facet.Facet]. + */ val rootEntityType: Class + + /** + * Facet type this extension point can serialization/deserialization. The result of deserialization, entities of the type declared + * at [rootEntityType] in the [com.intellij.workspaceModel.storage.EntitySource] + */ val supportedFacetType: String + + /** + * Method for deserialization [org.jetbrains.jps.model.serialization.facet.FacetState] read from external source. Facet state is + * an intermediate representation to avoid core communication with tags directly. + * @param builder storage to add read from facet state entities + * @param moduleEntity module to which these settings belong + * @param facetState intermediate representation of facet related data read out from external sources + * @param evaluateExternalSystemIdAndEntitySource function which should be invoked to get [com.intellij.workspaceModel.storage.EntitySource] + * for your entities and externalSystemId which should be stored somewhere in your entities + */ fun loadEntitiesFromFacetState(builder: MutableEntityStorage, moduleEntity: ModuleEntity, facetState: FacetState, evaluateExternalSystemIdAndEntitySource: (FacetState) -> Pair) + + /** + * Create intermediate representation from entities of declared at [rootEntityType] type which will be used for serialization on disk. + * @param entities list of certain type entities + * @param storeExternally indicator which tells where this data will be store, under `.idea` or in external system folder + */ fun createFacetStateFromEntities(entities: List, storeExternally: Boolean): List + + /** + * Method for creation facet XML tag from root type entity passed as a parameter + */ fun serializeIntoXml(entity: T): Element companion object { diff --git a/platform/workspaceModel/jps/src/com/intellij/workspaceModel/ide/impl/jps/serialization/DefaultFacetEntitySerializer.kt b/platform/workspaceModel/jps/src/com/intellij/workspaceModel/ide/impl/jps/serialization/DefaultFacetEntitySerializer.kt index c9b72da401c2..e72605178af1 100644 --- a/platform/workspaceModel/jps/src/com/intellij/workspaceModel/ide/impl/jps/serialization/DefaultFacetEntitySerializer.kt +++ b/platform/workspaceModel/jps/src/com/intellij/workspaceModel/ide/impl/jps/serialization/DefaultFacetEntitySerializer.kt @@ -10,7 +10,6 @@ import com.intellij.workspaceModel.storage.bridgeEntities.addFacetEntity import com.intellij.workspaceModel.storage.bridgeEntities.api.* import org.jdom.Element import org.jetbrains.jps.model.serialization.facet.FacetState -import java.util.function.Consumer class DefaultFacetEntitySerializer: CustomFacetRelatedEntitySerializer { override val rootEntityType: Class