IntelliJ Platform Plugin SDK Help

Entity Properties

Properties of an entity are declared by properties in its interface.

Supported Types

The following types of properties are supported:

  • primitive types (Int, Boolean, etc);

  • String

  • enum

  • an immutable data class with properties of supported types

  • a sealed class which implementations are immutable data classes or objects with properties of supported types

  • a nullable variant of a supported type

  • List, Set or Map collections with elements of supported types

  • VirtualFileUrl

  • EntitySource

  • SymbolicEntityId

  • a reference to another entity

Property Kinds

There are three kinds of properties: mandatory, optional, and computable properties.

Mandatory

If a property doesn't have a default value, it is treated as mandatory. Values of mandatory properties must be provided when an entity is created by passing parameters to the companion object.

Optional

If a property has a default value, it is treated as optional. Optional properties may be initialized in a lambda passed as the last parameter to the companion object. To provide a default value for a property, define a getter for them and annotate it with @Default. Properties whose type is nullable have a default value null implicitly, but there are no implicit default values for other types, even for primitive types.

Computable

If a property has a getter without @Default annotation, it is treated as computable. Computable properties may use data from other properties of the same entity and other entities referenced from it only. Their values aren't saved in the storage but computed each time a property is accessed.

VirtualFileUrl

Represents a reference to a file or directory. Workspace model entities must use properties of this type to store references to files instead of storing their paths or URLs as String properties, because it consumes less memory and provides an efficient way to locate a VirtualFile.

Use VirtualFileUrlManager.getOrCreateFromUrl() or VirtualFile.toVirtualFileUrl() extension function to create an instance.

Use VirtualFileUrl.virtualFile extension property to locate a VirtualFile from a VirtualFileUrl.

Also, it is possible to automatically update references in entities when corresponding files are moved or renamed. Currently, it is implemented to specific types of entities only in VirtualFileUrlWatcher.

Locating VirtualFileUrl

EntityStorage.getVirtualFileUrlIndex() provides a way to quickly find entities referring to a particular VirtualFileUrl.

val workspaceModel = WorkspaceModel.getInstance(project) // Getting VirtualFileUrlManager val virtualFileManager = workspaceModel.getVirtualFileUrlManager() // Find or create VirtualFileUrl from the URL val path = virtualFileManager.findByUrl("file:///foo/bar") // Searching all entities with the path workspaceModel.currentSnapshot.getVirtualFileUrlIndex() .findEntitiesByUrl(path)

EntitySource

Describes a place where an entity came from. It may be:

  • a configuration file on disk (property EntitySource.virtualFileUrl should be implemented to point to that file)

  • some strings that identify the source

  • a marker object if entities are generated automatically by some algorithm which doesn't use external data

Each entity must specify its source via the WorkspaceEntity.entitySource property. For example, entities loaded from configuration files under the .idea directory use JpsProjectFileEntitySource.

Information about entity sources is used by the MutableEntityStorage.replaceBySource() function. See also Replace By Source section.

An entity source must be serializable along with entities, so there are some restrictions on its implementation. It must be a data class that contains read-only properties of the following types:

  • primitive types

  • String

  • enum

  • List of another allowed type

  • another data class with properties of the allowed types

  • sealed abstract class where all implementations satisfy these requirements

SymbolicEntityId

The ID is supposed to be based on names specified by a user in the UI or in configuration files, not on some autogenerated IDs. Can be interpreted as a business key for the concrete entity type. SymbolicEntityId for an entity may include a reference to SymbolicEntityId of its parent entity if it is necessary to make them unique. For example, ModuleId includes only the name of the module, but FacetId includes the name of the facet and a reference to ModuleId of the module the facet belongs to.

The Workspace Model store is prohibited from containing two entities with the same SymbolicEntityId. For example, SymbolicIdAlreadyExistsException will be thrown when trying to add a ModuleEntity with a ModuleId that is already present in the EntityStorage.

To specify a SymbolicEntityId for an entity, the entity interface must extend WorkspaceEntityWithSymbolicId and provide an implementation of its symbolicId property which returns an instance of the corresponding class. Type parameter <E> of the returned instance must be equal to the entity interface. A specific implementation of SymbolicEntityId may be used in properties of an entity to create a symbolic link to an entity of the corresponding type.

Note that unlike parent-child "hard" links, symbolic links aren't guaranteed to be resolvable. When a linked entity is removed, the entity referring to it doesn't change, and the ID will resolve to null. However, if after modification of an entity, its symbolicId changes, properties of all other entities containing a value equal to the previous ID are automatically replaced with the new ID.

The storage maintains an index, which can be used to quickly find all entities referring to a given entity via symbolic links, see EntityStorage.referrers() function.

An implementation must be a data class which contains read-only properties of the following types:

  • primitive types

  • String

  • enum

  • another data class with properties of the allowed types

  • sealed abstract class where all implementations satisfy these requirements

References between Entities

There are two ways to refer from one entity to another: using parent-child relationship (hard links) and via symbolic references (soft links).

Parent-Child Relationship

Some types of entities may be connected by "parent-child" relationship. It is introduced by a property in the parent entity interface which refers to the child entity (entities) with @Child annotation, and a property in the child entity interface which refers to the parent entity. For example, content roots are defined inside a module in the project model, so ContentRootEntity is defined as a child of ModuleEntity.

The storage automatically maintains the consistency of this relationship during modifications:

  • if a parent entity is removed, all its child entities are also removed

  • if a child entity is removed, the corresponding property in its parent entity is updated so it no longer refers to the removed entity

The property referring to child entity may have a type

  • @Child ChildEntity? indicating that there are zero or one child entities of the given type

  • List<@Child ChildEntity> indicating that there are zero, one, or more child entities of the given type

The property referring to the parent entity may have a type

  • ParentEntity indicating that the parent is mandatory

  • ParentEntity? indicating that the parent is optional

If the parent is optional, it is possible to create a child entity without the parent entity and set the reference to the parent entity to null for an existing child entity. Also, if the reference to a child entity is removed from the corresponding property in the parent entity, it causes automatic removal of the child entity if it specifies that the parent is mandatory and sets the parent reference to null if the parent is optional.

See the "Parent-child relationship between entities" section in WorkspaceEntity code documentation for more details.

Symbolic References

If a configuration described by an entity refers to a configuration described by another entity, a symbolic reference may be used. It may happen that the referenced configuration doesn't exist: in that case the reference cannot be resolved.

To define a symbolic reference, the interface of the referenced entity must extend WorkspaceEntityWithSymbolicId, provide an implementation of SymbolicEntityId and return it from its symbolicId property. The entity which refers to it must store an instance of that SymbolicEntityId implementation in one of its properties.

For example, a module from the project model may depend on other modules. This is implemented by a property with the type ModuleId which is stored inside ModuleDependencyItem data class stored in the ModuleEntity.dependencies property.

The value of a property containing an implementation of SymbolicEntityId is automatically updated if the symbolicId property of the referenced entity changes after modification.

Last modified: 15 August 2024