The language repository, project modules, languages and models can be conveniently accessed programmatically through Open API. Open API gives you controlled access to the model and also allows you to provide your own implementations for some aspects, such as persistence.

These two usage types will be discussed individually.

Note: This document is meant to provide a general high-level overview of the Open API philosophy and give you useful starting links to the API. For technical details on how to use it please consult the API on GitHub.

Using Open API

The API is located under the org.jetbrains.mps.openapi package and is divided into five sub-packages:

language - provides a set of interfaces to gain access to compiled languages and inspect their structure

The API recognizes these logical elements, with the ones above containing the ones below in the list:

Repository

Module

Model

Node

Properties, References

Open API also recognizes meta-structure, which is orthogonal to the elements above. The meta-structure consists of the following key elements:

Language

Concept, Enumeration

Members (such as properties, links and enum literals)

Each node has an associated concept. A concept belongs to a Language. Languages may keep a pointer to the source module that they originated from to give the language user a way to investigate the language in detail.

The API enables you to browse the whole repository and investigate its modules, their models as well as the nodes that these models are built from. Additionally the API has capabilities to search for element's usages or find any element by its name irrespective of its location within the repository. You can also modify all of these elements, save your changes or reload them from a persistent storage. The API will detect colliding modifications to the model in memory and its persistent storage.

Using commands

Open API provides means to alter the models, as well. Modifications need to be performed as commands that are passed to the repository for processing. Typical model-changing/editing actions can be un-done/re-done, while actions performing major changes to the module structure cannot. There are three types of changes:

models and nodes can be changed through undoable actions

modules, their properties and dependencies can be performed through repository commands

radical changes to the project, such as a VCS update or a complete project reload, need an external update action to be performed - no node-level notifications are fired in such cases, onlymodel replacedormodule changednotifications are triggered.

The commands depending on their type will have all the necessary read or write permission assigned automatically before they start changing the model. Change notifications are fired to the registered listeners on the node, model, module or repository levels.

Concurrent access

Open API is designed for concurrent access and will correctly handle multiple threads accessing the models through Open API simultaneously, provided the supplied synchronization mechanisms are correctly utilized by the calling code. Open API will reject all improperly synchronized requests and thus preserve the integrity of the models.

In a more concrete terms, you need to obtain a read or write action before you start performing your operations, otherwise you'd get exceptions fired from the code.

Custom persistence

By default MPS stores models as flat files in an XML or binary format. To allow you to customize the way your models are persisted, Open API provides gives you several options.

Alternative file format

Changing the format of model data stored in a flat file is the simplest way to customize model persistence. You simply register your own ModelFactory with the file extension of your choice (through PersistenceFacade) and MPS will use that factory to instantiate your custom SModel implementations whenever that file extension is discovered. You'll also need to provide and register your own implementations of SModelIdFactory and SNodeIdFactory.

Alternative storage

If you're more adventurous and want, for example, to load your models from a database or other non-file storage, you need to additionally provide a ModelRootFactory, which can create custom ModelRoot instances. These model roots will then handle all the specifics of your chosen storage in order to load/save models. You may typically also need to bundle UI that would allow the users to configure data source details, such as database location, user name or other.

Icon

The xmlPersistence sample project that comes bundled with MPS shows a non-trivial example of implementing custom persistence. Check out the description of the Custom Persistence Cookbook for practical details on how to provide your own persistence to MPS models in MPS as well as IDEA.

Custom Find Usage and Navigation participants

Providing a custom implementation of FindUsagesParticipant will allow you to optimize FindUsages in models using your custom persistence. Similarly, custom implementations of NavigationParticipant will have a chance to optimize Go To Root/Class/Symbol actions. Instead of letting the default find usages and navigation implementations load all models into memory and process them in a standard way, by providing custom participants to PersistenceFacade you have the option to access the persistent storage directly and thus speed-up search as well as navigation.

No labels

1 Comment

I have been using my language inside IntelliJ Idea. I wanted to get hold on the objects created whilst I write my language. Can any body help me to get hold of the AST generated?I want to get hold of the modified object as soon as I Save my editor. I got a hint that I have to use Open API, but I have no clue as to how it works. Can anybody help me with that?