<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0"
     xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xml:base="https://specificlanguages.com/">
  <channel>
    <title>Writing an Importer on Specific Languages</title>
    <link>https://specificlanguages.com/series/writing-an-importer/</link>
    <description>Recent content in Writing an Importer on Specific Languages</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <managingEditor>sergej@specificlanguages.com (Sergej Koščejev)</managingEditor>
    <webMaster>sergej@specificlanguages.com (Sergej Koščejev)</webMaster>
    <lastBuildDate>Mon, 14 Feb 2022 09:43:53 +0100</lastBuildDate>
    
    <atom:link href="https://specificlanguages.com/series/writing-an-importer/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>Writing an importer: Updating existing nodes</title>
      <link>https://specificlanguages.com/posts/2022-02/14-writing-an-importer-updating-existing-nodes/</link>
      <pubDate>Mon, 14 Feb 2022 09:43:53 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-02/14-writing-an-importer-updating-existing-nodes/</guid>
      <description>&lt;p&gt;In the &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/25-writing-an-importer-error-handling-considerations/&#34;&gt;last post of the series&lt;/a&gt; I
promised that we will tackle reimports next. I got almost no feedback about my importer series so I thought must have
bored my readership to death. However, I recently got a message asking me whether I am going to continue the series. So,
today, as promised, we will enhance our importer to allow updating existing data.&lt;/p&gt;
&lt;p&gt;If we run the importer twice in its current state, we get the imported nodes duplicated. Simply clearing the model
before an import and recreating it during an import would avoid this problem, but would break any existing references to
the previously imported nodes, and this is usually not ideal.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-02/14-writing-an-importer-updating-existing-nodes/"><![CDATA[ <p>In the <a href="https://specificlanguages.com/posts/2022-01/25-writing-an-importer-error-handling-considerations/">last post of the series</a> I
promised that we will tackle reimports next. I got almost no feedback about my importer series so I thought must have
bored my readership to death. However, I recently got a message asking me whether I am going to continue the series. So,
today, as promised, we will enhance our importer to allow updating existing data.</p>
<p>If we run the importer twice in its current state, we get the imported nodes duplicated. Simply clearing the model
before an import and recreating it during an import would avoid this problem, but would break any existing references to
the previously imported nodes, and this is usually not ideal.</p>
<p>Thus, we are left with trying to find existing nodes in the model and updating them. To be able to do that, we need to
know whether a node matches an existing one. To do that, each such node has to carry a domain-specific unique
identifier.</p>
<p>Note that not every node has to have an identifier, only nodes that may be referenced from outside the import. The
identifiers don&rsquo;t have to be unique globally, usually uniqueness within a single concept (or item type) is enough.</p>
<p>Previously we have modified our importer to add all nodes to a temporary model first, and add them to the original model
in a separate step. We did this originally to avoid leaving the model in incosistent state on failure, but this
two-phase approach will also help us with our today&rsquo;s task.</p>
<p>We will modify the second step to &ldquo;synchronize&rdquo; or &ldquo;merge&rdquo; the temporary model into the target model, rather than
blindly add nodes.</p>
<p>The merge algorithm is an area that may become quite complicated depending on what kinds of changes you want to handle
between reimports. In our simple case it is enough to look through the model for an existing node and update it if it
exists, or create a new one:</p>
<figure><a href="https://specificlanguages.com/posts/2022-02/14-writing-an-importer-updating-existing-nodes/code.png">
    <img src="https://specificlanguages.com/posts/2022-02/14-writing-an-importer-updating-existing-nodes/code.png" width="640"/> </a>
</figure>

<p>(The code can be found <a href="https://github.com/specificlanguages/sample-importer">in the usual place on GitHub</a>.)</p>
<p>This code can also be optimized by building a map of existing nodes rather than searching through the model each time.
Otherwise, the simplistic approach works quite well. However, depending on your needs, you may need to modify your merge
algorithm to provide answers to these questions:</p>
<ul>
<li>
<p>What do we do with nodes that are present in the original model but not in the import? If these nodes are not supposed
to be referenced from outside the model, it is safe to delete them. Another option could be to mark them as hidden or
removed with a special boolean property. Or maybe even &ldquo;commenting them out&rdquo; using <a href="https://www.jetbrains.com/help/mps/commenting-out-nodes.html">MPS universal
comments</a>.</p>
</li>
<li>
<p>Is the order of certain child nodes within their parent important? If so, the child list may have to be rearranged
according to the incoming model.</p>
</li>
<li>
<p>What about forward references to child nodes, i.e. referring to a child node that does not exist yet. In this case you
might not be able to add it to the model when you encounter it (like we do in <code>findOrCreateLocation</code> in our example)
because its parent may not exist yet. You could perhaps hold them in some kind of a &ldquo;buffer&rdquo; first, and at the end,
check that the &ldquo;buffer&rdquo; nodes have all been added to the model (because they were visited from other nodes).</p>
</li>
</ul>
<p>These considerations are shared among many importers, and Daniel Binkele-Raible from itemis has been working on a
generic framework for merging models. It is currently undergoing review before it can be merged into MPS-extensions but
you can see the current state in this <a href="https://github.com/JetBrains/MPS-extensions/pull/487">pull request</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Writing an importer: Error handling considerations</title>
      <link>https://specificlanguages.com/posts/2022-01/25-writing-an-importer-error-handling-considerations/</link>
      <pubDate>Tue, 25 Jan 2022 09:23:38 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/25-writing-an-importer-error-handling-considerations/</guid>
      <description>&lt;p&gt;What can possibly go wrong when importing a JSON document? Several problems come to mind:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The input file can be unparseable as JSON. For example, the user has chosen a .zip file by mistake instead of .json.&lt;/li&gt;
&lt;li&gt;A valid JSON file can have unexpected structure. For example, the user has chosen a .json file with a different,
unrelated data set.&lt;/li&gt;
&lt;li&gt;The data set is correct but contains incorrect values. For example, a reference mentions an object that does not
exist, a field value is out of expected range, or some references form an unexpected cycle.&lt;/li&gt;
&lt;li&gt;The file could be fully valid but unexpectedly large.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We often want to avoid leaving the model in an inconsistent state after a failed import. While we can sometimes assume
that users are using version control and know how to roll back unintended changes, it is nicer if they don&amp;rsquo;t have to.
Our current importer logic adds the objects straight into the target model as we go through the parsed structure, and
as a result it is prone to this issue.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/25-writing-an-importer-error-handling-considerations/"><![CDATA[ <p>What can possibly go wrong when importing a JSON document? Several problems come to mind:</p>
<ol>
<li>The input file can be unparseable as JSON. For example, the user has chosen a .zip file by mistake instead of .json.</li>
<li>A valid JSON file can have unexpected structure. For example, the user has chosen a .json file with a different,
unrelated data set.</li>
<li>The data set is correct but contains incorrect values. For example, a reference mentions an object that does not
exist, a field value is out of expected range, or some references form an unexpected cycle.</li>
<li>The file could be fully valid but unexpectedly large.</li>
</ol>
<p>We often want to avoid leaving the model in an inconsistent state after a failed import. While we can sometimes assume
that users are using version control and know how to roll back unintended changes, it is nicer if they don&rsquo;t have to.
Our current importer logic adds the objects straight into the target model as we go through the parsed structure, and
as a result it is prone to this issue.</p>
<p>We can fix this by importing objects into a <em>temporary model</em> first, and only moving them to the target model once the
full import is complete. While in theory the move could partially fail as well, in practice this is unlikely as it all
happens in memory with data that has passed validation.</p>
<p>To create a temporary model we can make use of <code>jetbrains.mps.smodel.tempmodel.TemporaryModels</code> singleton:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>TemporaryModels temporaryModels <span style="color:#f92672">=</span> TemporaryModels.<span style="color:#a6e22e">getInstance</span>();
</span></span><span style="display:flex;"><span>model tempModel <span style="color:#f92672">=</span> temporaryModels.<span style="color:#a6e22e">createReadOnly</span>(TempModuleOptions.<span style="color:#a6e22e">forDefaultModule</span>());
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>  importDataIntoModel(sourceFile, tempModel);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  foreach root in tempModel.<span style="color:#a6e22e">roots</span>(<span style="color:#f92672">&lt;</span>all<span style="color:#f92672">&gt;</span>) {
</span></span><span style="display:flex;"><span>    targetModel.<span style="color:#a6e22e">add</span> <span style="color:#a6e22e">root</span>(root);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">finally</span> {
</span></span><span style="display:flex;"><span>  temporaryModels.<span style="color:#a6e22e">dispose</span>(tempModel);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>I have moved the original code into <code>importDataIntoModel</code> method that we now call to import the data into the temporary
model. After the import is successful, we add all roots to the target model. Adding a root to another model removes it
from its original model.</p>
<p>When creating a temporary model we have to choose whether it will be read-only or editable. This pertains to the ability
of the user to edit it via an editor, it is possible to modify the model via code in any case. We do not show the model
to the user and in any case we don&rsquo;t intend it to be modifiable by the user, so we choose the read-only option.</p>
<p>In the next post we will make it possible to update already existing data via import, rather than adding new objects on
each import. In the meantime, the code is <a href="https://github.com/specificlanguages/sample-importer">on GitHub</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Writing an importer: Invoking from the editor</title>
      <link>https://specificlanguages.com/posts/2022-01/18-writing-an-importer-invoking-from-editor/</link>
      <pubDate>Tue, 18 Jan 2022 11:38:09 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/18-writing-an-importer-invoking-from-editor/</guid>
      <description>&lt;p&gt;In the last &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/&#34;&gt;couple&lt;/a&gt; &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/&#34;&gt;of&lt;/a&gt; &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/14-writing-an-importer-adding-action-to-menu/&#34;&gt;posts&lt;/a&gt; in this series we have looked at invoking the importer
via an action from a menu. This works pretty well for a simple case where we only had to choose the input file.
Sometimes, however, we want give our users an interface to provide additional configuration to the importer to customize
how it behaves.&lt;/p&gt;
&lt;p&gt;We could do it by creating and showing a Swing dialog, but we can also create a concept to store the configuration in
the model and invoke the importer right from the editor of that concept by clicking a button.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/18-writing-an-importer-invoking-from-editor/"><![CDATA[ <p>In the last <a href="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/">couple</a> <a href="https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/">of</a> <a href="https://specificlanguages.com/posts/2022-01/14-writing-an-importer-adding-action-to-menu/">posts</a> in this series we have looked at invoking the importer
via an action from a menu. This works pretty well for a simple case where we only had to choose the input file.
Sometimes, however, we want give our users an interface to provide additional configuration to the importer to customize
how it behaves.</p>
<p>We could do it by creating and showing a Swing dialog, but we can also create a concept to store the configuration in
the model and invoke the importer right from the editor of that concept by clicking a button.</p>
<p>Here is how it could look like:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/18-writing-an-importer-invoking-from-editor/import-configuration-editor.png">
    <img src="https://specificlanguages.com/posts/2022-01/18-writing-an-importer-invoking-from-editor/import-configuration-editor.png" width="640"/> </a><figcaption>
            <h4>Import configuration editor</h4>
        </figcaption>
</figure>

<h1 id="why-editor">Why editor?</h1>
<p>Why use the editor instead of an action? By creating a separate configuration concept we can easily persist a
configuration so that the user does not need to re-enter it every time she wants to run an import. We can also store
multiple alternative configurations. An MPS editor is also easier to design than a Swing dialog.</p>
<h1 id="implementation">Implementation</h1>
<p>We create a separate language for import configuration, <code>game.importconfig</code>, to keep the core language clean of
infrastructure concerns. The configuration is represented by a single rootable concept, <code>ImportConfiguration</code>, with a
string property for the source file.</p>
<p>The editor of <code>ImportConfiguration</code> embeds a Swing button to invoke the import logic. The code for the button is shown
below:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/18-writing-an-importer-invoking-from-editor/component-provider.png">
    <img src="https://specificlanguages.com/posts/2022-01/18-writing-an-importer-invoking-from-editor/component-provider.png" width="640"/> </a><figcaption>
            <h4>Button implementation</h4>
        </figcaption>
</figure>

<p>The implementation is similar in spirit to the action implementation from the previous posts:</p>
<ul>
<li>We <a href="https://specificlanguages.com/posts/how-to-get-current-project-from-editor/">retrieve the current IDEA project</a> using the same
mechanism (DataManager and DataContext) that IDEA actions use internally.</li>
<li>The <code>actionPerformed</code> method will be invoked outside of any model access lock so we need to start a command manually
using the <code>command</code> statement from <code>jetbrains.mps.lang.access</code> language.</li>
<li>Any exception thrown is <a href="https://specificlanguages.com/posts/notification-balloons-instead-of-message-boxes/">shown using a notification</a>, same as in the action.</li>
</ul>
<p>Find the latest code on <a href="https://github.com/specificlanguages/sample-importer">GitHub</a>. The <code>game.sandbox.imported</code>
model contains a sample import configuration.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Writing an importer: Adding the action to a menu</title>
      <link>https://specificlanguages.com/posts/2022-01/14-writing-an-importer-adding-action-to-menu/</link>
      <pubDate>Fri, 14 Jan 2022 09:49:12 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/14-writing-an-importer-adding-action-to-menu/</guid>
      <description>&lt;p&gt;In our &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/&#34;&gt;last post&lt;/a&gt; working on the importer, we created an
action to invoke our importer. The action worked but wasn&amp;rsquo;t included in any menu. Today we will fix this.&lt;/p&gt;
&lt;p&gt;Each menu in MPS shows a particular &lt;em&gt;action group&lt;/em&gt;. Action groups are extensible, dynamic lists of actions whose
contents can be modified by plugins such as our importer plugin. They are also recursive: you can add an action group to
another action group.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/14-writing-an-importer-adding-action-to-menu/"><![CDATA[ <p>In our <a href="https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/">last post</a> working on the importer, we created an
action to invoke our importer. The action worked but wasn&rsquo;t included in any menu. Today we will fix this.</p>
<p>Each menu in MPS shows a particular <em>action group</em>. Action groups are extensible, dynamic lists of actions whose
contents can be modified by plugins such as our importer plugin. They are also recursive: you can add an action group to
another action group.</p>
<p>In fact, you cannot add a single action directly to an action group but have to wrap it in an action group of its own
first.</p>
<h1 id="finding-the-right-group">Finding the right group</h1>
<p>The biggest stumbling block here is finding the right group to add the action to. Although the
<a href="https://www.jetbrains.com/help/mps/plugin.html#actionsandactiongroups">documentation</a> states that the predefined MPS
groups are stored in the <code>jetbrains.mps.ide.actions</code> model, it is not really helpful in this instance.</p>
<p>First, the documentation is incomplete. For example, the <code>EditorPopup</code> group that represents the context menu in an
editor is defined in <code>jetbrains.mps.ide.editor.actions</code> (note the <code>.editor.</code> in the name).</p>
<p>Second, <code>jetbrains.mps.ide.actions</code> model contains dozens of actions, and the documentation does not specify which group
corresponds to which menu.</p>
<p>We can try guessing the name or looking through the completion menu. In our case we look for the context menu related to
a model so we could try typing <code>Model</code> and see what comes up.</p>
<p>If that fails, we can look up an action that is already present in the menu, and use <em>Find Usages</em> to find which groups
contain it. Ideally the action should be something distinct that would be easy to track, as opposed to a generic action
like <em>New</em> or <em>Build Model</em>.</p>
<p>Let&rsquo;s say we look through the menu and find an action called <em>Analyze Model Dependencies</em>. This is the action&rsquo;s title
but we need to find the actual action node whose name is going to be a Java identifier. So we need to guess again.</p>
<p>In this case, <code>AnalyzeModelDependencies</code> doesn&rsquo;t bring up any results but searching for just <code>Analyze</code> finds
<code>AnalyzeDependencies</code> which turns out to be the action group we are looking for.</p>
<p>The next step is to search for usages of <code>AnalyzeDependencies</code>, globally. To do that we bring up the <em>Find Usages
Settings</em> dialog by pressing <code>Ctrl+Alt+Shift+F7</code>, change the <em>Scope</em> to <em>Global</em> and confirm.</p>
<p>This brings us to <code>ModelActions</code> group that is the group we are looking for. It mostly consists of <em>anchors</em> (items with
an arrow in front of their names) that represent sections that other action groups can add actions to, to keep related
actions together.</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/14-writing-an-importer-adding-action-to-menu/model-actions-group.png">
    <img src="https://specificlanguages.com/posts/2022-01/14-writing-an-importer-adding-action-to-menu/model-actions-group.png" width="640"/> </a>
</figure>

<h1 id="adding-the-action-to-the-group">Adding the action to the group</h1>
<p>After we have figured out the action group, adding the action is fairly straightforward. I have recorded a screencast of
the necessary steps:</p>

			<div
					style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
				<iframe
					src="https://player.vimeo.com/video/665929764?dnt=0"
						style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allow="fullscreen">
				</iframe>
			</div>

<h1 id="next-invoking-importer-from-an-editor">Next: invoking importer from an editor</h1>
<p>In the next post we will implement an alternative way of invoking importers: from an editor. Meanwhile, the latest code
is <a href="https://github.com/specificlanguages/sample-importer">on GitHub</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Writing an importer: Action code</title>
      <link>https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/</link>
      <pubDate>Tue, 11 Jan 2022 09:00:00 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/</guid>
      <description>&lt;p&gt;Continuing where we left off &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/&#34;&gt;last&lt;/a&gt;, today we are ready to write some code in our action. We need to choose a file and
then invoke the importer logic on the file and the model.&lt;/p&gt;
&lt;h1 id=&#34;file-chooser&#34;&gt;File Chooser&lt;/h1&gt;
&lt;p&gt;Since MPS builds upon the IDEA Platform which builds upon Swing, we can use the usual Swing components as well as the
platform-provided facilities. I prefer to use classes from the platform when available because they often provide a more
polished look-and-feel. In this case the platform provides a class called &lt;code&gt;FileChooser&lt;/code&gt; for opening a &amp;ldquo;choose file&amp;rdquo;
dialog, documented &lt;a href=&#34;https://plugins.jetbrains.com/docs/intellij/file-and-class-choosers.html&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/"><![CDATA[ <p>Continuing where we left off <a href="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/">last</a>, today we are ready to write some code in our action. We need to choose a file and
then invoke the importer logic on the file and the model.</p>
<h1 id="file-chooser">File Chooser</h1>
<p>Since MPS builds upon the IDEA Platform which builds upon Swing, we can use the usual Swing components as well as the
platform-provided facilities. I prefer to use classes from the platform when available because they often provide a more
polished look-and-feel. In this case the platform provides a class called <code>FileChooser</code> for opening a &ldquo;choose file&rdquo;
dialog, documented <a href="https://plugins.jetbrains.com/docs/intellij/file-and-class-choosers.html">here</a>.</p>
<h1 id="projects">Projects</h1>
<p>To invoke the <code>FileChooser</code> we will need to pass to it the current project instance. There are in fact several different
project classes in MPS, one coming from IDEA (<code>com.intellij.openapi.project.Project</code>) and another one coming from MPS
(<code>jetbrains.mps.project.MPSProject</code>). In fact, there are other project classes in MPS but we will not need to concern
ourselves with them in this tutorial.</p>
<p>To obtain an instance of the IDEA project we add a context parameter of type Project to our action. I like to call it
<code>ideaProject</code> to disambiguate it from a MPS project.</p>
<p>Here is what the <code>FileChooser</code> invocation looks like:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>VirtualFile chosenFile <span style="color:#f92672">=</span> FileChooser.<span style="color:#a6e22e">chooseFile</span>(
</span></span><span style="display:flex;"><span>  FileChooserDescriptorFactory.<span style="color:#a6e22e">createSingleFileDescriptor</span>(<span style="color:#e6db74">&#34;json&#34;</span>), <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">ideaProject</span>, <span style="color:#66d9ef">null</span>);
</span></span></code></pre></div><h1 id="virtual-file-system">Virtual file system</h1>
<p>The result we get from the dialog is a <code>VirtualFile</code> object. Virtual files are abstractions provided by the IDEA
platform&rsquo;s virtual file system (VFS). We are not going to use VFS in our code though, so we will escape the abstraction
immediately by calling <code>getPath()</code> on it.</p>
<p>Next, we need to make sure that we are not calling the file chooser while holding a model lock.</p>
<h1 id="model-access-reading-writing-and-commanding">Model access: reading, writing, and commanding</h1>
<p>All model access in MPS takes place under a read-write lock. The rules are documented in the <a href="https://www.jetbrains.com/help/mps/open-api-accessing-models-from-code.html#usingcommands">MPS
help</a>. In our case, the
importer modifies the model so it will need to be invoked from within a <em>command</em>.</p>
<p>By default, an action&rsquo;s <code>execute</code> block is wrapped in a command automatically and we don&rsquo;t have to think of this. On the
other hand, UI calls are <em>disallowed</em> when under a read-write lock to prevent UI freezes and deadlocks. This forces us
to manage model locking explicitly in our code so we need to disable the automatic command by changing <code>required access</code>
from <code>command</code> to <code>none</code>.</p>
<p>If we now need to execute a piece of code in a command, we do it explicitly by using the <code>command</code> statement from
<code>jetbrains.mps.lang.access</code> language that was helpfully added to the model for us by the wizard that created the plugin
solution.</p>
<p>We need to pass a <em>repository</em> to the command, which we can obtain from the current MPS project, which we get from the
context like the IDEA project above.</p>
<h1 id="show-error-messages-as-balloons">Show error messages as balloons</h1>
<p>Our importer logic can throw exceptions that we should show to the user. If we don&rsquo;t do anything and simply rethrow the
exception, it will cause a flashing exclamation mark to appear in the bottom right corner. If we catch the exception we
can use a plain Swing message box to show it, or, even better, a notification. I have written previously about
notifications <a href="https://specificlanguages.com/posts/notification-balloons-instead-of-message-boxes/">here</a>.</p>
<h1 id="the-complete-code">The complete code</h1>
<p>Here is what the action looks like after all the modifications:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/final-code.png">
    <img src="https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/final-code.png" width="640"/> </a>
</figure>

<h1 id="next-adding-the-action-to-a-menu">Next: adding the action to a menu</h1>
<p>The action works now but can still only be invoked via the <em>Find Action</em> popup (<code>Ctrl/Cmd+Shift+A</code>). In the next post we
will add it to a menu.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Writing an importer: Actions</title>
      <link>https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/</link>
      <pubDate>Mon, 10 Jan 2022 10:55:11 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/</guid>
      <description>&lt;p&gt;In our &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/&#34;&gt;last post&lt;/a&gt; we have created a simple importer that we
could invoke from the MPS Console. Today we&amp;rsquo;ll start implementing a way for our users to invoke the importer.&lt;/p&gt;
&lt;p&gt;A common way of extending MPS is via &lt;a href=&#34;https://www.jetbrains.com/help/mps/plugin.html&#34;&gt;plugins&lt;/a&gt; that provide &lt;em&gt;actions&lt;/em&gt;.
An action is a piece of code that can be invoked via a menu, such as the main menu or a context menu, or a key
combination. In the context of MPS it is important to distinguish these so called &lt;em&gt;plugin actions&lt;/em&gt; from &lt;em&gt;editor actions&lt;/em&gt;
which are invoked by the MPS editor to manipulate the node being edited. Both types of actions can be provided by the
language developer but plugin actions can perform any task and have access to the entire IDE, whereas editor actions
only have access to the current editor and the node being edited.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/"><![CDATA[ <p>In our <a href="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/">last post</a> we have created a simple importer that we
could invoke from the MPS Console. Today we&rsquo;ll start implementing a way for our users to invoke the importer.</p>
<p>A common way of extending MPS is via <a href="https://www.jetbrains.com/help/mps/plugin.html">plugins</a> that provide <em>actions</em>.
An action is a piece of code that can be invoked via a menu, such as the main menu or a context menu, or a key
combination. In the context of MPS it is important to distinguish these so called <em>plugin actions</em> from <em>editor actions</em>
which are invoked by the MPS editor to manipulate the node being edited. Both types of actions can be provided by the
language developer but plugin actions can perform any task and have access to the entire IDE, whereas editor actions
only have access to the current editor and the node being edited.</p>
<p>To launch the importer our action will need two pieces of input from the user: the source file and the target model. Our
plan will be to take the target model from the context and use the built-in file chooser dialog to ask for the source
file.</p>
<h1 id="plugin-solution">Plugin solution</h1>
<p>Whereas we were free to put our Java code (almost) anywhere we like, we are more constrained with plugins and actions.
They must reside in a specially configured solution in a specially named model. MPS provides a wizard that can create
both the solution and the model for us, found in the context menu of the project (<em>New → Plugin Solution</em>):</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/new-plugin-solution.png">
    <img src="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/new-plugin-solution.png" width="640"/> </a>
</figure>

<p>The wizard will perform three tasks for us:</p>
<ol>
<li>
<p>Create a solution and set its <em>Solution Kind</em> set to <em>Other</em>. You can also manually set the solution kind on the Java
tab in the solution properties dialog:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/solution-kind.png">
       <img src="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/solution-kind.png" width="640"/> </a>
   </figure>

</li>
<li>
<p>Create a model named <code>&lt;solution-name&gt;.plugin</code> with a few useful languages already imported for use.</p>
</li>
<li>
<p>Create a root node of concept <code>StandalonePluginDescriptor</code>.</p>
</li>
</ol>
<p>These three arcane bits of configuration are required for MPS to load actions and other plugin elements from the
solution. More details about MPS classloading and solution kinds can be found on a helpful <a href="https://github.com/mbeddr/mbeddr.core/wiki/MPS:-Deps-and-Classpath">Wiki
page</a> of the mbeddr project.</p>
<h1 id="creating-an-action">Creating an action</h1>
<p>We can now create our first action in the model (<em>right click on the model → New → j.m.lang.plugin → Action</em>). Let&rsquo;s
call it <code>ImportLocationsFromJson</code> and set the caption to &ldquo;Import Locations from JSON&hellip;&rdquo;. The ellipsis at the end is a
convention to indicate that the action will show a dialog.</p>
<p>As a quick check that things so far have been configured properly, after we build the action we should be able to use
the &ldquo;Find Action&rdquo; popup (<code>Ctrl/Cmd+Shift+A</code>) to look it up:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/find-action.png">
    <img src="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/find-action.png" width="640"/> </a>
</figure>

<p>You can even try invoking it from that popup (press <code>Enter</code>): it should do nothing at all (including throwing no
errors).</p>
<p>NOTE: Actions are documented in the <a href="https://www.jetbrains.com/help/mps/plugin.html#actionsandactiongroups">JetBrains MPS
Help</a> and I suggest you read the documentation to
gain a better understanding of what we are going to do.</p>
<h1 id="getting-the-target-model-context-parameters">Getting the target model: context parameters</h1>
<p>Our action will be placed in a context menu of a model. It can determine the model that it is invoked on via a context
parameter with key <code>MODEL</code>. To use this key we need to import the model that contains <code>MPSCommonDataKeys</code> class,
<code>jetbrains.mps.ide.actions@java_stub</code>:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/mps-common-data-keys.png">
    <img src="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/mps-common-data-keys.png" width="640"/> </a>
</figure>

<p>If MPS does not give the name to the parameter automatically, name it <code>model</code>. Leave it required.</p>
<p>Adding a required parameter serves two purposes: we will be able to use it in the <code>execute</code> block, and the action will
be disabled if invoked from a context where no model is present. If you now try looking up the action again via
<code>Cmd/Ctrl+Shift+A</code>, you will find that the action is disabled if the editor is focused and enabled if the focus is on a
model in the Logical View (project tree).</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/import-locations-disabled.png">
    <img src="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/import-locations-disabled.png" width="640"/> </a><figcaption>
            <h4>&#34;Import Locations...&#34; action is disabled when the focus is currently in the editor</h4>
        </figcaption>
</figure>

<h1 id="next-code">Next: code!</h1>
<p>Having the model means we have one half of our inputs without writing any code yet. In our next part we will have no
such luxury and will get our hands dirty with Java code to let the user pick the input file and perform the action.
Meanwhile, the code is <a href="https://github.com/specificlanguages/sample-importer">on GitHub</a> and I&rsquo;m eager to hear your
reactions and thoughts about the tutorial so far.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Writing an importer: The basics</title>
      <link>https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/</link>
      <pubDate>Fri, 07 Jan 2022 12:00:00 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/</guid>
      <description>&lt;p&gt;&lt;em&gt;NOTE: Due to an error, only a single paragraph was sent by email from the &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/06-writing-an-importer-introduction/&#34;&gt;first post&lt;/a&gt; in this series. I apologize for any inconvenience caused and
hope it will work better this time.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Continuing from our &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/06-writing-an-importer-introduction/&#34;&gt;last post&lt;/a&gt;, let&amp;rsquo;s have a brief look
at the data and the language we are going to work with and set things up. Remember, the code is &lt;a href=&#34;https://github.com/specificlanguages/sample-importer&#34;&gt;available on
GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;the-language&#34;&gt;The language&lt;/h1&gt;
&lt;p&gt;I have created a very small language for this tutorial, called &lt;code&gt;game.core&lt;/code&gt;. It consists of a single concept named
&lt;code&gt;Location&lt;/code&gt;, representing a location in a game:&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/"><![CDATA[ <p><em>NOTE: Due to an error, only a single paragraph was sent by email from the <a href="https://specificlanguages.com/posts/2022-01/06-writing-an-importer-introduction/">first post</a> in this series. I apologize for any inconvenience caused and
hope it will work better this time.</em></p>
<p>Continuing from our <a href="https://specificlanguages.com/posts/2022-01/06-writing-an-importer-introduction/">last post</a>, let&rsquo;s have a brief look
at the data and the language we are going to work with and set things up. Remember, the code is <a href="https://github.com/specificlanguages/sample-importer">available on
GitHub</a>.</p>
<h1 id="the-language">The language</h1>
<p>I have created a very small language for this tutorial, called <code>game.core</code>. It consists of a single concept named
<code>Location</code>, representing a location in a game:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/location-structure.png">
    <img src="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/location-structure.png" width="640"/> </a>
</figure>

<p>In text-based adventures each location is connected to other locations, for example via compass directions (north, east,
south, west). To keep things simple but still interesting in our case, each location has just one optional <code>next</code>
reference pointing to the next location in the adventure.</p>
<p>Each location also has a <code>name</code> (inherited from <code>INamedConcept</code>) and a <code>description</code>. A location is uniquely identified
by its name. The <code>description</code> field holds additional information about the location.</p>
<h1 id="the-input">The input</h1>
<p>We will be importing JSON data such as this file (available in the GitHub repository as
<a href="https://github.com/specificlanguages/sample-importer/blob/main/input/adventure.json"><code>input/adventure.json</code></a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;locations&#34;</span>: [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;Street&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;description&#34;</span>: <span style="color:#e6db74">&#34;You stand in front of an old house that looks just like you remembered it in the past. The entrance door is ajar.&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;next&#34;</span>: <span style="color:#e6db74">&#34;House Entrance&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;House Entrance&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;description&#34;</span>: <span style="color:#e6db74">&#34;You enter the house. Everything here is covered with dust: the floor, the hanger, the shelves. Except for one dust-free door with a label on it that says \&#34;Cellar\&#34;.&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;next&#34;</span>: <span style="color:#e6db74">&#34;Cellar&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;Cellar&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;description&#34;</span>: <span style="color:#e6db74">&#34;The cellar is dark and you can&#39;t see anything. Your right hand rests on the wall and senses a light switch.&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Note that a location refers to its next location by its (unique) name. Also note that we can refer to locations that
appear later in the file (i.e. the importer must support <em>forward references</em>).</p>
<h1 id="gson">Gson</h1>
<p>To parse JSON data we will use the <a href="https://github.com/google/gson">Gson</a> library from Google. To do that we will need
to add the Gson JAR to our project. The process of doing so is
<a href="https://www.jetbrains.com/help/mps/getting-the-dependencies-right.html#addingexternaljavaclassesandjarstoaproject-runtimesolutions">well</a>
<a href="https://tomassetti.me/how-to-add-jars-and-resources-in-a-jetbrains-mps-project/">documented</a>
<a href="https://www.youtube.com/watch?v=P7NvsMxMNPM">elsewhere</a> and I will not cover it here. In the linked project</p>
<p>Now we can test our parser from the <a href="https://www.jetbrains.com/help/mps/mps-console.html">MPS Console</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">&gt;</span> {
</span></span><span style="display:flex;"><span>    JsonElement element; 
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">try</span> (Reader reader <span style="color:#f92672">=</span> Files.<span style="color:#a6e22e">newBufferedReader</span>(Path.<span style="color:#a6e22e">of</span>(<span style="color:#e6db74">&#34;/path/to/sample-importer/input/adventure.json&#34;</span>))) { 
</span></span><span style="display:flex;"><span>      element <span style="color:#f92672">=</span> JsonParser.<span style="color:#a6e22e">parseReader</span>(reader); 
</span></span><span style="display:flex;"><span>    } 
</span></span><span style="display:flex;"><span>    <span style="color:#960050;background-color:#1e0010">#</span>print element;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// output:</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// {&#34;locations&#34;:[{&#34;name&#34;:&#34;Street&#34;,&#34;description&#34;:&#34;You stand in front of an old house ...</span>
</span></span></code></pre></div><p>Since it can be a bit difficult for newcomers to MPS to figure out how to type things properly, here is a screencast of me typing this code into MPS Console and running it:</p>

			<div
					style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
				<iframe
					src="https://player.vimeo.com/video/663267049?dnt=0"
						style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allow="fullscreen">
				</iframe>
			</div>

<h1 id="where-to-put-the-code">Where to put the code</h1>
<p>The code of the importer will live in a Java class but where do we put the class? We have several options, ordered by
the amount of effort required:</p>
<ol>
<li>Put the class in one of the existing aspects of the <code>game.core</code> language.</li>
<li>Create a separate model in the <code>game.core</code> and put the class there. Such a model would be called &ldquo;utility model&rdquo; in
MPS parlance.</li>
<li>Create a separate solution for the importer logic, create a model there and put the class into it.</li>
</ol>
<p>We don&rsquo;t need to agonize too much about the decision because we can always move classes between models and modules.
Since none of the existing language aspects feel appropriate, we create a new model, name it <code>game.core.importer</code>, and
a new class <code>ImporterLogic</code> inside:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/import-logic-created.png">
    <img src="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/import-logic-created.png" width="640"/> </a>
</figure>

<h1 id="initial-code">Initial code</h1>
<p>We are now finally ready to write some code! Since our input data contains forward references, we store all nodes in a
hash map, keyed by their names. If we encounter a reference to a node we haven&rsquo;t seen yet, we create it but only
initialize its name. Later when we encounter the full data for the target location, we take the previously created node
from the map and fill it in.</p>
<p>Here is the full code:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ImportLogic</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">importData</span>(string sourceFile, model targetModel) <span style="color:#66d9ef">throws</span> IOException {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> JsonElement element;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">try</span> (Reader reader <span style="color:#f92672">=</span> Files.<span style="color:#a6e22e">newBufferedReader</span>(Path.<span style="color:#a6e22e">of</span>(sourceFile))) {
</span></span><span style="display:flex;"><span>      element <span style="color:#f92672">=</span> JsonParser.<span style="color:#a6e22e">parseReader</span>(reader);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    JsonArray locationsArray <span style="color:#f92672">=</span> element.<span style="color:#a6e22e">getAsJsonObject</span>().<span style="color:#a6e22e">get</span>(<span style="color:#e6db74">&#34;locations&#34;</span>).<span style="color:#a6e22e">getAsJsonArray</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Store locations in a map to resolve forward references</span>
</span></span><span style="display:flex;"><span>    Map<span style="color:#f92672">&lt;</span>string, node<span style="color:#f92672">&lt;</span>Location<span style="color:#f92672">&gt;&gt;</span> locationsByName <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> HashMap<span style="color:#f92672">&lt;</span>string, node<span style="color:#f92672">&lt;</span>Location<span style="color:#f92672">&gt;&gt;</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    foreach locationElement in locationsArray {
</span></span><span style="display:flex;"><span>      JsonObject locationObject <span style="color:#f92672">=</span> locationElement.<span style="color:#a6e22e">getAsJsonObject</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      string name <span style="color:#f92672">=</span> locationObject.<span style="color:#a6e22e">get</span>(<span style="color:#e6db74">&#34;name&#34;</span>).<span style="color:#a6e22e">getAsString</span>();
</span></span><span style="display:flex;"><span>      string description <span style="color:#f92672">=</span> locationObject.<span style="color:#a6e22e">get</span>(<span style="color:#e6db74">&#34;description&#34;</span>).<span style="color:#a6e22e">getAsString</span>();
</span></span><span style="display:flex;"><span>      JsonElement nextElement <span style="color:#f92672">=</span> locationObject.<span style="color:#a6e22e">get</span>(<span style="color:#e6db74">&#34;next&#34;</span>);
</span></span><span style="display:flex;"><span>      string nextName <span style="color:#f92672">=</span> nextElement <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span> <span style="color:#f92672">?</span> <span style="color:#66d9ef">null</span> : nextElement.<span style="color:#a6e22e">getAsString</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      node<span style="color:#f92672">&lt;</span>Location<span style="color:#f92672">&gt;</span> location <span style="color:#f92672">=</span> locationsByName.<span style="color:#a6e22e">computeIfAbsent</span>(name, {string name <span style="color:#f92672">=&gt;</span> <span style="color:#f92672">&lt;</span>Location(name: name)<span style="color:#f92672">&gt;</span>; });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      node<span style="color:#f92672">&lt;</span>Location<span style="color:#f92672">&gt;</span> nextLocation <span style="color:#f92672">=</span> nextName <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span> <span style="color:#f92672">?</span> <span style="color:#66d9ef">null</span> : locationsByName.<span style="color:#a6e22e">computeIfAbsent</span>(nextName, {string name <span style="color:#f92672">=&gt;</span> <span style="color:#f92672">&lt;</span>Location(name: name)<span style="color:#f92672">&gt;</span>; });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      location.<span style="color:#a6e22e">description</span> <span style="color:#f92672">=</span> description;
</span></span><span style="display:flex;"><span>      location.<span style="color:#a6e22e">next</span> <span style="color:#f92672">=</span> nextLocation;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      targetModel.<span style="color:#a6e22e">add</span> <span style="color:#a6e22e">root</span>(location);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h1 id="running-the-importer">Running the importer</h1>
<p>We don&rsquo;t have any user-friendly way to run the importer yet (I will cover it in a later post) but we can already run it
from the MPS Console. Before we do that we need to create a target model for the imported nodes. Let&rsquo;s put it into the
<code>game.sandbox</code> solution and name it <code>game.sandbox.imported</code>. Next, we can invoke the importer from the console as
follows:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/import-from-mps-console.png">
    <img src="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/import-from-mps-console.png" width="640"/> </a>
</figure>

<p>The console shows an error because we don&rsquo;t catch IOException but the code will compile and run. As a result, you should
see the three locations created in the <code>game.sandbox.imported</code> model.</p>
<p>Note that since the code in <code>ImportLogic#importData()</code> manipulates the target model, it must run in the context of a
<a href="https://www.jetbrains.com/help/mps/open-api-accessing-models-from-code.html#usingcommands"><em>command</em></a>. MPS Console
establishes a command for us automatically, in other cases we may need to start a command manually.</p>
<p>In the next post we will look at invoking the importer in a user-friendly way. Until then, I would be happy to get any
feedback and questions you have.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Writing an importer: Introduction</title>
      <link>https://specificlanguages.com/posts/2022-01/06-writing-an-importer-introduction/</link>
      <pubDate>Thu, 06 Jan 2022 11:18:00 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/06-writing-an-importer-introduction/</guid>
      <description>&lt;p&gt;What do you do if you have some data that you want to import into MPS? There is no built-in importer in MPS, nor is
there a ready-to-use framework, so it can be overwhelming for a beginning MPS language developer to piece together all
the required knowledge of MPS subsystems.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s why I want to start a series of beginner-level posts to cover the process of writing an importer. I will assume
you have experience programming in Java but are just starting with MPS.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/06-writing-an-importer-introduction/"><![CDATA[ <p>What do you do if you have some data that you want to import into MPS? There is no built-in importer in MPS, nor is
there a ready-to-use framework, so it can be overwhelming for a beginning MPS language developer to piece together all
the required knowledge of MPS subsystems.</p>
<p>That&rsquo;s why I want to start a series of beginner-level posts to cover the process of writing an importer. I will assume
you have experience programming in Java but are just starting with MPS.</p>
<p>Our domain is going to be text-based adventure games, also known as <a href="https://en.wikipedia.org/wiki/Interactive_fiction">interactive
fiction</a>. We will use a very simple language for describing a very
simple adventure game, and import game data from a JSON file into MPS. I am not going to cover the development of the
language itself in this series and will only focus on the importing process.</p>
<p>I will be using the latest currently released version of MPS, 2021.2.3, and I will publish my code <a href="https://github.com/specificlanguages/sample-importer">on
GitHub</a> as we go, so you can follow along. The linked repository
already contains our simple language, a sandbox where I have manually created a few game locations, and a sample JSON
input file with the map data. I will talk more about the language in the next post.</p>
<p>In the upcoming posts I am going to cover these topics:</p>
<ul>
<li><strong>The basics.</strong> Using Google Gson from MPS to read a JSON file and using the MPS SModel API to create MPS nodes from
the data.</li>
<li><strong>Invoking the importer.</strong> Various ways in which you can launch the import from within MPS.</li>
<li><strong>Supporting subsequent reimports.</strong> How to update the model when the input data changes.</li>
<li><strong>Error handling.</strong> Letting the user know if there was a problem with the input data.</li>
</ul>
<p>The list of topics isn&rsquo;t set in stone and I may come up with additional topics as we go. But if there&rsquo;s an interesting
aspect related to importers that you want me to cover, let me know. You can hit Reply if you are reading this as an
email or email me directly at <a href="mailto:sergej@specificlanguages.com">sergej@specificlanguages.com</a>).</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
