<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://lupa.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://lupa.dev/" rel="alternate" type="text/html" /><updated>2026-05-12T05:38:35+00:00</updated><id>https://lupa.dev/feed.xml</id><title type="html">lupa.dev</title><subtitle>Personal website of Fabian Lupa</subtitle><author><name>Fabian Lupa</name></author><entry><title type="html">Managing Updates to abapGit in Shared Development Systems</title><link href="https://lupa.dev/2023/09/22/managing-updates-to-abapgit-in-shared-development-systems/" rel="alternate" type="text/html" title="Managing Updates to abapGit in Shared Development Systems" /><published>2023-09-22T00:00:00+00:00</published><updated>2023-09-22T00:00:00+00:00</updated><id>https://lupa.dev/2023/09/22/managing-updates-to-abapgit-in-shared-development-systems</id><content type="html" xml:base="https://lupa.dev/2023/09/22/managing-updates-to-abapgit-in-shared-development-systems/"><![CDATA[<p>If you are using abapGit as a tool to facilitate your development processes you at some point have to decide on an update strategy for abapGit itself. Most likely you are only updating abapGit when a feature or bug fix is released that you need or file diffs are displayed that are related to format changes that you want to get rid off. (Or you feel guilty not having updated for a while…) This is a bit of a shame as new features are added all the time and keeping abapGit up-to-date from a technical perspective is <a href="https://docs.abapgit.org/user-guide/getting-started/upgrade.html">extremely easy</a>. There are no notes to implement, no support packages to import, no migrations to do and no dependencies to worry about. Issues rather arise because of processes and communication between teams regarding ongoing developments when you are sharing systems.</p>

<h1 id="format-changes">Format Changes</h1>

<p>abapGit’s core feature is mapping development objects of the ABAP Platform to files and vice versa. By serializing and deserializing development objects general compatibility with standard git is achieved which can then be utilized by the git protocol implementation. The format of these serialized development objects is <em>human readable</em> <a href="https://github.com/abapGit/abapGit#design-goals">by design</a> and intentionally mostly handcrafted in the object handler of each supported object type. This approach allows for reviewing changes with standard tools outside the SAP ecosystem (git platforms, gerrit, …) and even developing ABAP without a backend system altogether just based on the editable files. This does however have one annoying side effect:</p>

<p><strong>Changes to the serialization logic will have an impact on the file representation of an ABAP package as a git repository, without any changes being done to the development objects themselves.</strong></p>

<p>To git this behavior is very unusual as normally no mapping is required and developers edit the actual files directly or at least the git client has no influence at all on the files’ contents.</p>

<p>This can even happen without changes to the object handlers. New fields may be added to standard structures serialized by abapGit in a new SAP release that may or may not be relevant for the file.</p>

<p>Case in point: Can you tell this repository is perfectly installed and <a href="https://github.com/SAP-samples/abap-platform-refscen-flight/issues/16">up-to-date</a>?</p>

<figure class=""><a href="format-changes.png" class="image-popup" title="Figure 1: Repository with diffs caused by format changes
"><img src="format-changes.png" alt="Screenshot of Repository with diffs caused by format changes" /></a><figcaption id="figure-format-changes">
      Figure 1: Repository with diffs caused by format changes

    </figcaption></figure>

<h1 id="consequences-for-the-development-process">Consequences for the Development Process</h1>

<p>As a developer, this is rather frustrating. You just want to commit your changes and quickly review them on the staging screen. If abapGit was updated in the meantime and relevant format changes were done or support for new object types was added that are part of your package, the staging screen includes these in addition to your changes. This can be especially annoying if you edited the same objects that were just affected by the format change. Then you cannot even filter by transport or username to find your actual changes. To correctly only commit your change, you have to sift through the list and in the worst case commit partial changes to a file with git patch. You are more likely to just commit these format changes with your actual changes making the history a bit less understandable and annoying anyone who has to review the pull request or resolve merge conflicts later.</p>

<blockquote>
  <p>No one likes having to review and account for changes that are unrelated to the issue being worked on.</p>
</blockquote>

<p>If that doesn’t really bother you or your developers, that’s totally fine. If it does or you would at least like to offer some options for those who are, they are some strategies you can consider.</p>

<p>The usual approach to avoid this issue would be to simply not update abapGit if any development efforts are still in progress. Or at least if format changes are listed in the change log / commit log. Update abapGit when all branches of all repos in the system are merged and then make a pull request for each repository just including the format changes where review can be skipped. This can be done on individual developer systems or project systems where the changes being done are very isolated, either to a developer or to specific projects. In my experience the far more common scenario is shared development systems with multiple mostly unrelated development teams doing changes in parallel in the same system.</p>

<p>Let’s discuss some approaches to deal with format changes in abapGit in this shared development system landscape.</p>

<h1 id="manually-managing-updates">Manually Managing Updates</h1>

<p>You can of course make yourself the one responsible for the updates and gather all development teams at a round table in a monthly interval. Chances that a common time frame for an update is found are slim especially when the ratio between the number of development teams and development systems increases. If only each team or repository could use the version of abapGit they require independently of one another in the same system and update at their convenience. Unfortunately, you cannot have multiple active versions of the same development objects on the ABAP Platform… Except with abapGit itself you can!</p>

<h1 id="using-the-standalone-version-as-a-fallback">Using the Standalone Version As a Fallback</h1>

<p>abapGit is delivered in two flavors:</p>

<ol>
  <li>Developer Version</li>
  <li>Standalone Version</li>
</ol>

<p>While the developer version uses global objects where only one active version can exist, the standalone version is contained in a single report. You can install as many standalone versions in parallel as you wish. One of the only objects that is shared between the versions is the database table <em>ZABAPGIT</em>, meaning you can access the same data from all installed versions. Now you can simply update the developer version frequently and point anyone requiring an older version to a fitting older standalone version installation. They are even motivated to migrate back as parallel serialization is only supported in the developer version…</p>

<h1 id="implementing-exits-for-convenience">Implementing Exits for Convenience</h1>

<p>To keep track of which abapGit version should be used for which repository and why you can implement <a href="https://docs.abapgit.org/user-guide/reference/exits.html">exits</a> for convenience. The wall-exits allow for displaying messages in a specific repository or on the repository list.</p>

<figure class=""><a href="repository-header.png" class="image-popup" title="Figure 2: Adjusted repository header
"><img src="repository-header.png" alt="Screenshot of adjusted repository header" /></a><figcaption id="figure-repository-header">
      Figure 2: Adjusted repository header

    </figcaption></figure>

<p>You could for example first check if the repo belongs to the git host your company uses and then differentiate based on the repository name:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">METHOD</span> <span class="n">zif_abapgit_exit</span><span class="o">~</span><span class="n">wall_message_repo</span><span class="p">.</span>
  <span class="k">DATA</span> <span class="n">full_name</span> <span class="k">TYPE</span> <span class="k">string</span><span class="p">.</span>

  <span class="k">TRY</span><span class="p">.</span>
      <span class="k">IF</span>    <span class="n">is_repo_meta</span><span class="o">-</span><span class="n">offline</span> <span class="o">=</span> <span class="n">abap_true</span>
         <span class="k">OR</span> <span class="nb">to_lower</span><span class="p">(</span> <span class="n">zcl_abapgit_url</span><span class="o">=&gt;</span><span class="n">host</span><span class="p">(</span> <span class="n">is_repo_meta</span><span class="o">-</span><span class="n">url</span> <span class="p">)</span> <span class="p">)</span>
            <span class="o">&lt;&gt;</span> <span class="s1">'https://github.com'</span><span class="p">.</span>
        <span class="k">RETURN</span><span class="p">.</span>
      <span class="k">ENDIF</span><span class="p">.</span>
      <span class="n">full_name</span> <span class="o">=</span> <span class="nb">to_lower</span><span class="p">(</span>
        <span class="nb">condense</span><span class="p">(</span> <span class="n">cl_http_utility</span><span class="o">=&gt;</span><span class="n">unescape_url</span><span class="p">(</span>
                    <span class="n">zcl_abapgit_url</span><span class="o">=&gt;</span><span class="n">path_name</span><span class="p">(</span> <span class="n">is_repo_meta</span><span class="o">-</span><span class="n">url</span> <span class="p">)</span> <span class="p">)</span> <span class="p">)</span> <span class="p">).</span>
    <span class="k">CATCH</span> <span class="n">zcx_abapgit_exception</span> <span class="k">INTO</span> <span class="k">DATA</span><span class="p">(</span><span class="k">exception</span><span class="p">).</span>
      <span class="n">ii_html</span><span class="o">-&gt;</span><span class="n">add</span><span class="p">(</span>
        <span class="p">|</span><span class="o">&lt;</span><span class="k">div</span> <span class="k">class</span><span class="o">=</span><span class="c1">"panel error"&gt;Error parsing URL { is_repo_meta-url }: | &amp;&amp;</span>
        <span class="p">|{</span> <span class="k">exception</span><span class="o">-&gt;</span><span class="n">get_text</span><span class="p">(</span> <span class="p">)</span> <span class="p">}</span><span class="o">&lt;/</span><span class="k">div</span><span class="o">&gt;</span><span class="p">|</span> <span class="p">).</span>
      <span class="k">RETURN</span><span class="p">.</span>
  <span class="k">ENDTRY</span><span class="p">.</span>

  <span class="k">CASE</span> <span class="n">full_name</span><span class="p">.</span>
    <span class="k">WHEN</span> <span class="s1">'/fabianlupa/abapgit-exit-example'</span><span class="p">.</span>
      <span class="n">ii_html</span><span class="o">-&gt;</span><span class="n">add</span><span class="p">(</span> <span class="p">|</span><span class="o">&lt;</span><span class="n">hr</span><span class="o">/&gt;</span><span class="p">|</span> <span class="p">).</span>
      <span class="n">ii_html</span><span class="o">-&gt;</span><span class="n">add</span><span class="p">(</span> <span class="p">|</span><span class="o">&lt;</span><span class="n">span</span><span class="o">&gt;</span><span class="p">|</span> <span class="p">).</span>
      <span class="n">ii_html</span><span class="o">-&gt;</span><span class="n">add_icon</span><span class="p">(</span> <span class="s1">'info-circle-solid/blue'</span> <span class="p">).</span>
      <span class="n">ii_html</span><span class="o">-&gt;</span><span class="n">add</span><span class="p">(</span>
        <span class="p">|</span><span class="n">Please</span> <span class="n">use</span> <span class="n">the</span> <span class="n">abapGit</span> <span class="n">standalone</span> <span class="k">version</span> <span class="k">for</span> <span class="n">this</span> <span class="n">repo</span> <span class="k">for</span> <span class="n">now</span><span class="p">|</span> <span class="p">).</span>
      <span class="n">ii_html</span><span class="o">-&gt;</span><span class="n">add</span><span class="p">(</span> <span class="p">|</span><span class="o">&lt;/</span><span class="n">span</span><span class="o">&gt;</span><span class="p">|</span> <span class="p">).</span>
      <span class="n">ii_html</span><span class="o">-&gt;</span><span class="n">add</span><span class="p">(</span> <span class="p">|</span><span class="o">&lt;</span><span class="n">hr</span><span class="o">/&gt;</span><span class="p">|</span> <span class="p">).</span>
  <span class="k">ENDCASE</span><span class="p">.</span>
<span class="k">ENDMETHOD</span><span class="p">.</span>
</code></pre></div></div>

<p><span class="listing-caption" id="listing-custom-message">Listing 1: Exit implementation to add a custom message in the repository view
</span></p>

<p>You can also offer a link to execute the other abapGit installation and directly open the specific repository.</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CONSTANTS</span><span class="p">:</span>
  <span class="k">BEGIN</span> <span class="k">OF</span> <span class="n">actions</span><span class="p">,</span>
    <span class="n">launch_standalone</span> <span class="k">TYPE</span> <span class="k">string</span> <span class="k">VALUE</span> <span class="s1">'zlaunch'</span><span class="p">,</span>
  <span class="k">END</span> <span class="k">OF</span> <span class="n">actions</span><span class="p">.</span>

<span class="k">METHOD</span> <span class="n">zif_abapgit_exit</span><span class="o">~</span><span class="n">wall_message_repo</span><span class="p">.</span>
  <span class="p">(...)</span>
  <span class="n">ii_html</span><span class="o">-&gt;</span><span class="n">add</span><span class="p">(</span>
    <span class="p">|</span><span class="n">Please</span> <span class="n">use</span> <span class="n">the</span> <span class="n">abapGit</span> <span class="n">standalone</span> <span class="k">version</span> <span class="k">for</span> <span class="n">this</span> <span class="n">repo</span> <span class="k">for</span> <span class="n">now</span><span class="p">|</span> <span class="p">).</span>
  <span class="n">ii_html</span><span class="o">-&gt;</span><span class="n">add_a</span><span class="p">(</span>
    <span class="n">iv_txt</span>   <span class="o">=</span> <span class="s1">'Link'</span>
    <span class="n">iv_act</span>   <span class="o">=</span> <span class="n">actions</span><span class="o">-</span><span class="n">launch_standalone</span>
    <span class="n">iv_query</span> <span class="o">=</span> <span class="p">|</span><span class="k">program</span><span class="o">=</span><span class="n">ZABAPGIT_STANDALONE_20230802</span><span class="o">&amp;</span><span class="n">repo</span><span class="o">=</span><span class="p">{</span> <span class="n">is_repo_meta</span><span class="o">-</span><span class="k">key</span> <span class="p">}|</span> <span class="p">).</span>
  <span class="p">(...)</span>
<span class="k">ENDMETHOD</span><span class="p">.</span>

<span class="k">METHOD</span> <span class="n">zif_abapgit_exit</span><span class="o">~</span><span class="n">on_event</span><span class="p">.</span>
  <span class="k">DATA</span> <span class="k">program</span>  <span class="k">TYPE</span> <span class="n">progname</span><span class="p">.</span>
  <span class="k">DATA</span> <span class="n">repo_key</span> <span class="k">TYPE</span> <span class="n">rfc_spagpa</span><span class="o">-</span><span class="n">parval</span><span class="p">.</span>

  <span class="k">CASE</span> <span class="n">ii_event</span><span class="o">-&gt;</span><span class="n">mv_action</span><span class="p">.</span>
    <span class="k">WHEN</span> <span class="n">actions</span><span class="o">-</span><span class="n">launch_standalone</span><span class="p">.</span>
      <span class="k">program</span> <span class="o">=</span> <span class="n">ii_event</span><span class="o">-&gt;</span><span class="n">query</span><span class="p">(</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">get</span><span class="p">(</span> <span class="s1">'program'</span> <span class="p">).</span>
      <span class="n">repo_key</span> <span class="o">=</span> <span class="n">ii_event</span><span class="o">-&gt;</span><span class="n">query</span><span class="p">(</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">get</span><span class="p">(</span> <span class="s1">'repo'</span> <span class="p">).</span>
      <span class="k">IF</span> <span class="k">program</span> <span class="k">IS</span> <span class="k">INITIAL</span> <span class="k">OR</span> <span class="n">repo_key</span> <span class="k">IS</span> <span class="k">INITIAL</span> <span class="k">OR</span>
         <span class="k">program</span> <span class="k">NP</span> <span class="s1">'ZABAPGIT_STANDALONE*'</span><span class="p">.</span>
        <span class="n">zcx_abapgit_exception</span><span class="o">=&gt;</span><span class="n">raise</span><span class="p">(</span>
          <span class="p">|</span><span class="n">Unknown</span> <span class="n">jump</span> <span class="n">location</span> <span class="k">for</span> <span class="p">{</span> <span class="n">ii_event</span><span class="o">-&gt;</span><span class="n">mv_action</span> <span class="p">}|</span> <span class="p">).</span>
      <span class="k">ENDIF</span><span class="p">.</span>

      <span class="k">SET</span> <span class="k">PARAMETER</span> <span class="k">ID</span> <span class="n">zif_abapgit_definitions</span><span class="o">=&gt;</span><span class="n">c_spagpa_param_repo_key</span>
          <span class="k">FIELD</span> <span class="n">repo_key</span><span class="p">.</span>
      <span class="k">SUBMIT</span> <span class="p">(</span><span class="k">program</span><span class="p">)</span> <span class="k">AND</span> <span class="k">RETURN</span><span class="p">.</span>
      <span class="k">MESSAGE</span> <span class="p">|</span><span class="n">Returned</span> <span class="k">from</span> <span class="p">{</span> <span class="k">program</span> <span class="p">}|</span> <span class="k">TYPE</span> <span class="s1">'S'</span><span class="p">.</span>

      <span class="n">rs_handled</span><span class="o">-</span><span class="k">state</span> <span class="o">=</span> <span class="n">zcl_abapgit_gui</span><span class="o">=&gt;</span><span class="n">c_event_state</span><span class="o">-</span><span class="n">no_more_act</span><span class="p">.</span>
  <span class="k">ENDCASE</span><span class="p">.</span>
<span class="k">ENDMETHOD</span><span class="p">.</span>
</code></pre></div></div>

<p><span class="listing-caption" id="listing-redirection-link">Listing 2: Exit implementation to handle a redirection link
</span></p>

<p>Or you could even automatically redirect when opening the repository.</p>

<figure class=""><a href="redirect.gif" class="image-popup" title="Figure 3: Redirection on opening a repository
"><img src="redirect.gif" alt="Screen recording of redirection on opening a repository" /></a><figcaption id="figure-redirect">
      Figure 3: Redirection on opening a repository

    </figcaption></figure>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">METHOD</span> <span class="n">zif_abapgit_exit</span><span class="o">~</span><span class="n">on_event</span><span class="p">.</span>
  <span class="k">IF</span> <span class="k">NOT</span> <span class="p">(</span> <span class="n">ii_event</span><span class="o">-&gt;</span><span class="n">mv_current_page_name</span> <span class="o">=</span>
           <span class="s1">'ZCL_ABAPGIT_GUI_PAGE_REPO_OVER'</span>
           <span class="k">AND</span> <span class="n">ii_event</span><span class="o">-&gt;</span><span class="n">mv_action</span> <span class="o">=</span> <span class="s1">'select'</span> <span class="p">).</span>
    <span class="k">RETURN</span><span class="p">.</span>
  <span class="k">ENDIF</span><span class="p">.</span>

  <span class="k">DATA</span><span class="p">(</span><span class="n">repo_key</span><span class="p">)</span> <span class="o">=</span> <span class="k">EXACT</span> <span class="n">rfc_spagpa-parval</span><span class="p">(</span> <span class="n">ii_event</span><span class="o">-&gt;</span><span class="n">query</span><span class="p">(</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">get</span><span class="p">(</span> <span class="s1">'key'</span> <span class="p">)</span> <span class="p">).</span>
  <span class="k">DATA</span><span class="p">(</span><span class="n">repo</span><span class="p">)</span> <span class="o">=</span> <span class="n">zcl_abapgit_repo_srv</span><span class="o">=&gt;</span><span class="n">get_instance</span><span class="p">(</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">get</span><span class="p">(</span> <span class="k">EXACT</span> <span class="o">#</span><span class="p">(</span> <span class="n">repo_key</span> <span class="p">)</span> <span class="p">).</span>
  <span class="k">IF</span> <span class="n">repo</span><span class="o">-&gt;</span><span class="n">is_offline</span><span class="p">(</span> <span class="p">).</span>
    <span class="k">RETURN</span><span class="p">.</span>
  <span class="k">ENDIF</span><span class="p">.</span>

  <span class="k">DATA</span><span class="p">(</span><span class="n">online_repo</span><span class="p">)</span> <span class="o">=</span> <span class="k">CAST</span> <span class="n">zcl_abapgit_repo_online</span><span class="p">(</span> <span class="n">repo</span> <span class="p">).</span>
  <span class="k">DATA</span><span class="p">(</span><span class="n">full_name</span><span class="p">)</span> <span class="o">=</span> <span class="nb">to_lower</span><span class="p">(</span>
      <span class="nb">condense</span><span class="p">(</span> <span class="n">cl_http_utility</span><span class="o">=&gt;</span><span class="n">unescape_url</span><span class="p">(</span>
        <span class="n">zcl_abapgit_url</span><span class="o">=&gt;</span><span class="n">path_name</span><span class="p">(</span> <span class="n">online_repo</span><span class="o">-&gt;</span><span class="n">get_url</span><span class="p">(</span> <span class="p">)</span> <span class="p">)</span> <span class="p">)</span> <span class="p">)</span> <span class="p">).</span>

  <span class="k">DATA</span><span class="p">(</span><span class="k">program</span><span class="p">)</span> <span class="o">=</span> <span class="k">SWITCH</span> <span class="o">#</span><span class="p">(</span> <span class="n">full_name</span>
                            <span class="k">WHEN</span> <span class="s1">'/fabianlupa/abapgit-exit-example'</span>
                            <span class="k">THEN</span> <span class="s1">'ZABAPGIT_STANDALONE_20230802'</span> <span class="p">).</span>
  <span class="k">IF</span> <span class="k">program</span> <span class="k">IS</span> <span class="k">INITIAL</span><span class="p">.</span>
    <span class="k">RETURN</span><span class="p">.</span>
  <span class="k">ENDIF</span><span class="p">.</span>

  <span class="k">DATA</span><span class="p">(</span><span class="n">answer</span><span class="p">)</span> <span class="o">=</span> <span class="n">zcl_abapgit_ui_factory</span><span class="o">=&gt;</span><span class="n">get_popups</span><span class="p">(</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">popup_to_confirm</span><span class="p">(</span>
    <span class="n">iv_titlebar</span>      <span class="o">=</span> <span class="s1">'Redirect'</span>
    <span class="n">iv_text_question</span> <span class="o">=</span>
      <span class="p">|</span><span class="n">You</span> <span class="n">will</span> <span class="n">be</span> <span class="n">redirected</span> <span class="k">to</span> <span class="k">program</span> <span class="p">{</span> <span class="k">program</span> <span class="p">}</span> <span class="k">for</span> <span class="n">this</span> <span class="n">repository</span><span class="p">.|</span>
    <span class="n">iv_icon_button_1</span> <span class="o">=</span> <span class="k">CONV</span> <span class="o">#</span><span class="p">(</span> <span class="n">icon_okay</span> <span class="p">)</span>
    <span class="n">iv_text_button_1</span> <span class="o">=</span> <span class="s1">'Continue'</span>
    <span class="n">iv_text_button_2</span> <span class="o">=</span> <span class="s1">'Open here'</span> <span class="p">).</span>
  <span class="k">CASE</span> <span class="n">answer</span><span class="p">.</span>
    <span class="k">WHEN</span> <span class="s1">'1'</span><span class="p">.</span>
      <span class="k">SET</span> <span class="k">PARAMETER</span> <span class="k">ID</span> <span class="n">zif_abapgit_definitions</span><span class="o">=&gt;</span><span class="n">c_spagpa_param_repo_key</span>
          <span class="k">FIELD</span> <span class="n">repo_key</span><span class="p">.</span>
      <span class="k">SUBMIT</span> <span class="p">(</span><span class="k">program</span><span class="p">)</span> <span class="k">AND</span> <span class="k">RETURN</span><span class="p">.</span>
      <span class="k">MESSAGE</span> <span class="p">|</span><span class="n">Returned</span> <span class="k">from</span> <span class="p">{</span> <span class="k">program</span> <span class="p">}|</span> <span class="k">TYPE</span> <span class="s1">'S'</span><span class="p">.</span>
      <span class="n">rs_handled</span><span class="o">-</span><span class="k">state</span> <span class="o">=</span> <span class="n">zcl_abapgit_gui</span><span class="o">=&gt;</span><span class="n">c_event_state</span><span class="o">-</span><span class="n">no_more_act</span><span class="p">.</span>
    <span class="k">WHEN</span> <span class="s1">'2'</span><span class="p">.</span>
      <span class="n">rs_handled</span><span class="o">-</span><span class="k">state</span> <span class="o">=</span> <span class="n">zcl_abapgit_gui</span><span class="o">=&gt;</span><span class="n">c_event_state</span><span class="o">-</span><span class="n">not_handled</span><span class="p">.</span>
    <span class="k">WHEN</span> <span class="s1">'A'</span><span class="p">.</span>
      <span class="n">rs_handled</span><span class="o">-</span><span class="k">state</span> <span class="o">=</span> <span class="n">zcl_abapgit_gui</span><span class="o">=&gt;</span><span class="n">c_event_state</span><span class="o">-</span><span class="n">no_more_act</span><span class="p">.</span>
  <span class="k">ENDCASE</span><span class="p">.</span>
<span class="k">ENDMETHOD</span><span class="p">.</span>
</code></pre></div></div>

<p><span class="listing-caption" id="listing-automatic-redirection">Listing 3: Exit implementation to offer automatic redirection on opening a repository
</span></p>

<p>Note: The last implementation requires a recent (2023-09-18) <a href="https://github.com/abapGit/abapGit/pull/6498">change</a> in abapGit.</p>

<p>You can find the exit implementations for reference in this repository: <a href="https://github.com/fabianlupa/abapgit-exit-example">abapgit-exit-example</a></p>

<h1 id="keeping-track-of-installed-versions">Keeping Track of Installed Versions</h1>

<p>To keep track of which abapGit versions are installed in the system and when they were last updated you can install <a href="https://github.com/fabianlupa/abapgit-launcher">abapGit Launcher</a>, which provides a list of all available versions in the system and a central entry point.</p>

<figure class=""><a href="abapgit-launcher.png" class="image-popup" title="Figure 4: abapGit Launcher entry screen
"><img src="abapgit-launcher.png" alt="Screenshot of abapGit Launcher entry screen" /></a><figcaption>
      Figure 4: abapGit Launcher entry screen

    </figcaption></figure>

<h1 id="conclusion">Conclusion</h1>

<p>Ideally you wouldn’t need to do any of this and just update abapGit at a regular interval. This can still be done if you don’t mind the occasional diffs on the staging screen. Also the amount of times format changes happen has decreased significantly over the years as the most used object handlers are very stable by now. If format changes happen, they are usually bundled and merged together to minimize the impact as well as listed in the change log.</p>

<p>In addition, there are some other aspects which play into this topic not covered by this blog. For example, how these diffs are handled in CI/CD processes and how the repositories themselves regularly receive file updates caused by updates to abapGit. Or how the abapGit installation delivered in SAP BTP ABAP Environment and SAP S/4HANA Public Cloud may act differently compared to the open source one…</p>

<p>Regardless of the problem at hand you can easily see with the approaches listed above that abapGit is very adjustable and you do not necessarily need to change your processes to fit the software. The changes and additions didn’t even need to modify existing abapGit code, which would also have been entirely possible. Should you choose the latter, please consider <a href="https://docs.abapgit.org/development-guide/read-first/contributing.html">contributing</a> your changes so others may also benefit.</p>]]></content><author><name>Fabian Lupa</name></author><category term="developer tools" /><category term="ABAP" /><category term="abapGit" /><category term="shared development systems" /><summary type="html"><![CDATA[If you are using abapGit as a tool to facilitate your development processes you at some point have to decide on an update strategy for abapGit itself. Most likely you are only updating abapGit when a feature or bug fix is released that you need or file diffs are displayed that are related to format changes that you want to get rid off. (Or you feel guilty not having updated for a while…) This is a bit of a shame as new features are added all the time and keeping abapGit up-to-date from a technical perspective is extremely easy. There are no notes to implement, no support packages to import, no migrations to do and no dependencies to worry about. Issues rather arise because of processes and communication between teams regarding ongoing developments when you are sharing systems.]]></summary></entry><entry><title type="html">Installing AS ABAP Developer Edition with Hyper-V</title><link href="https://lupa.dev/2018/09/06/installing-as-abap-developer-edition-with-hyper-v/" rel="alternate" type="text/html" title="Installing AS ABAP Developer Edition with Hyper-V" /><published>2018-09-06T00:00:00+00:00</published><updated>2018-09-06T00:00:00+00:00</updated><id>https://lupa.dev/2018/09/06/installing-as-abap-developer-edition-with-hyper-v</id><content type="html" xml:base="https://lupa.dev/2018/09/06/installing-as-abap-developer-edition-with-hyper-v/"><![CDATA[<p class="notice--warning"><strong>Note:</strong> This post is from 2018. SAP now provides newer Developer Edition systems as Docker images. For current installations, it is recommended to check out the <a href="https://hub.docker.com/r/sapse/abap-cloud-developer-trial">ABAP Cloud Developer Trial</a> on Docker Hub, where you can find the latest installation instructions.</p>

<p>If you want to install the AS ABAP Developer Edition, one choice you have to make is picking the virtualization software that manages the virtual machine. Common options are</p>

<ul>
  <li>Oracle VirtualBox</li>
  <li>Docker</li>
  <li>VMware workstation</li>
  <li>Hyper-V</li>
</ul>

<p>There are several guides on VirtualBox and Docker, so I thought why not try out Hyper-V. Hyper-V is a virtualization solution that is included in Windows Pro/Professional versions and up (including Education). It is disabled by default, as it is more intended to be something you use on a server and have permanently running than using it on a development machine. (But we are going to do just that)</p>

<p>I ran into several problems when I first tried it so I thought a how-to guide might be helpful.</p>

<h1 id="enabling-hyper-v">Enabling Hyper-V</h1>

<p>To enable Hyper-V you need to open the “Turn Windows features on or off” dialog (accessible using the start menu) and check all the Hyper-V options.</p>

<p class="notice--warning">Once you enable Hyper-V some other virtualization software will stop working. This includes VirtualBox (and the Android emulator). To get these running you need to uncheck these options again.</p>

<figure class="half">
  <a href="windows-features1.png" class="image-popup" title="Figure 1: Windows Features
"><img src="windows-features1.png" alt="Screenshot of Windows Features in start menu search" /></a><a href="windows-features2.png" class="image-popup" title="Figure 1: Windows Features
"><img src="windows-features2.png" alt="Screenshot Windows Features settings dialog" /></a><figcaption>
      Figure 1: Windows Features

    </figcaption></figure>

<p>After a reboot you should now be able to open the Hyper-V Manager. If that doesn’t work or you run into errors you might need to enable hardware virtualization support in your BIOS.</p>

<h1 id="setting-up-the-local-network">Setting up the local network</h1>

<p>Before creating the new virtual machine some things about networking: If you want your application server to connect to the internet there are some additional steps to do.</p>

<p>Hyper-V has 3 options for virtual switches that you can attach to the virtual machine.</p>

<p><strong>External</strong> will bind the virtual machine somewhat directly to a “normal” network adapter (of for example a physical network card) of your choosing. That means if the network adapter provides internet connectivity the virtual machine will be able to access it. That also means that the virtual machine gets its own external IP address and might be directly accessible from other points of the network. In the network adapter settings of Windows you will see a new Bridged-adapter that connects the virtual switch to the physical adapter.</p>

<p><strong>Internal</strong> means the virtual machine will be able to connect to the host machine and to other VMs on that switch.</p>

<p><strong>Private</strong> means the VM can only access other VMs that use that switch too.</p>

<p>None of these options seem to fit for the use case in my opinion. I do not want the VM to get an external IP address. It should have a fixed one and not one given by some DHCP server that belongs to the Wifi network I happen to be in currently. Also I might have an AS ABAP installed on my laptop and one on my PC which are both on the same network. Since the hostname has to be vhcalnplci there would be a hostname clash in the network. But the other options do not provide an internet connection (which I need for abapGit). So what’s left?</p>

<p>In VirtualBox there is a NAT-Switch for this case, which will provide a local network that is able to access the internet. Fortunately Hyper-V has this type of switch as well, but there’s no GUI for it (yet). So you can either pick External with its drawbacks or use an elevated powershell prompt and practice your command line skills for later:</p>

<figure class=""><a href="powershell1.png" class="image-popup" title="Figure 2: Network configuration in PowerShell
"><img src="powershell1.png" alt="Screenshot of network configuration in PowerShell" /></a><figcaption>
      Figure 2: Network configuration in PowerShell

    </figcaption></figure>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PS</span><span class="w"> </span><span class="nx">C:\</span><span class="err">&gt;</span><span class="w"> </span><span class="nx">New-VMSwitch</span><span class="w"> </span><span class="err">–</span><span class="nx">SwitchName</span><span class="w"> </span><span class="s2">"NATSwitch"</span><span class="w"> </span><span class="err">–</span><span class="nx">SwitchType</span><span class="w"> </span><span class="nx">Internal</span><span class="w">
</span><span class="n">PS</span><span class="w"> </span><span class="nx">C:\</span><span class="err">&gt;</span><span class="w"> </span><span class="nx">New-NetIPAddress</span><span class="w"> </span><span class="err">–</span><span class="nx">IPAddress</span><span class="w"> </span><span class="nx">192.168.99.1</span><span class="w"> </span><span class="nt">-PrefixLength</span><span class="w"> </span><span class="nx">24</span><span class="w"> </span><span class="nt">-InterfaceAlias</span><span class="w"> </span><span class="s2">"vEthernet (NATSwitch)"</span><span class="w">
</span><span class="n">PS</span><span class="w"> </span><span class="nx">C:\</span><span class="err">&gt;</span><span class="w"> </span><span class="nx">New-NetNat</span><span class="w"> </span><span class="err">–</span><span class="nx">Name</span><span class="w"> </span><span class="nx">NATNetwork</span><span class="w"> </span><span class="err">–</span><span class="nx">InternalIPInterfaceAddressPrefix</span><span class="w"> </span><span class="nx">192.168.99.0/24</span><span class="w">
</span></code></pre></div></div>

<p>These commands might only work on recent updates of Windows 10 as it is quite new.</p>

<p>Going though these commands:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">New-VMSwitch</code> will create a named network adapter that is registered in Hyper-V as a virtual switch. You could do this part in the GUI if you wanted to</li>
  <li><code class="language-plaintext highlighter-rouge">New-NetIPAddress</code> will assign the Gateway IP address / subnet of the newly created switch.</li>
  <li><code class="language-plaintext highlighter-rouge">New-NetNat</code> finally creates a local NAT network for which you assign the IP address range it provides</li>
</ul>

<p>In my example I now have the following setup:</p>

<ul>
  <li>Gateway of the NAT network: 192.168.99.1</li>
  <li>NAT network: 192.168.99.0/24 (255.255.255.0)</li>
  <li>IP address of the AS ABAP VM: 192.168.99.10 (this is just an IP in the range I chose)</li>
</ul>

<p>I recommend picking an IP range that is unlikely to clash with any other parts of your network.</p>

<h1 id="creating-the-virtual-machine">Creating the virtual machine</h1>

<p>Back to GUIs and configuration wizards: Open Hyper-V Manager, right click on your computers hostname on the left and pick New -&gt; Virtual Machine.</p>

<figure class=""><a href="hyperv1.png" class="image-popup" title="Figure 3: New Virtual Machine Wizard in Hyper-V Manager
"><img src="hyperv1.png" alt="Screenshot of New Virtual Machine Wizard in Hyper-V Manager" /></a><figcaption>
      Figure 3: New Virtual Machine Wizard in Hyper-V Manager

    </figcaption></figure>

<p>Navigate through the dialogs, some recommendations:</p>

<ul>
  <li>Pick generation 2</li>
  <li>Assign at least 6 GB of memory (6144 MB)</li>
  <li>IMPORTANT: Uncheck dynamic memory. If dynamic memory is used the installation of the ASE database will fail as it tries to allocate more memory than is currently available. You can enable it again later on if you want to, but I had some problems with that.</li>
  <li>If you are using the NATSwitch do not connect it yet to the VM (pick “Not connected” instead). This is just to prevent the Linux installer to freeze on boot as it tries to contact a DHCP server (I think). The other options (Default-Switch or External) can be connected directly.</li>
  <li>I recommend using ~150 GB as a hard disk. It is quite a bit of effort to expand it later on so some buffer should be useful. I tend to copy all the installation files of the AS ABAP into the VM to prevent filesystem issues that may occur when installing over a mounted “Windows network share” with CIFS, so there needs to be some space for that too.</li>
  <li>In the operating system dialog you can already select the ISO or just do it later.</li>
</ul>

<p>After that you should have a new virtual machine in the list. Right click on it and open the settings. Under “Processor” you might want to adjust the number of virtual processors in some way to the amount of physical CPU cores you have. I don’t know what the best ratio is here. I just noticed with 1 processor assigned the maximum physical CPU usage I get is 25% with an i7. So I put it up to 4.</p>

<h1 id="installing-the-operating-system">Installing the Operating System</h1>

<p>Once the virtual machine is created you can install the operation system. I always pick OpenSUSE Leap because it seems to work the best for me. Download the ISO from <a href="https://software.opensuse.org/distributions/leap">here</a>.</p>

<p>Go into the settings again, this time under SCSI Controller (which should be installed by default for the virtual hard drive) add a DVD drive and attach the SUSE-ISO file to it.</p>

<figure class=""><a href="hyperv2.png" class="image-popup" title="Figure 4: Mounting an ISO image file in the virtual machine settings
"><img src="hyperv2.png" alt="Screenshot of virtual machine settings in Hyper-V Manager" /></a><figcaption>
      Figure 4: Mounting an ISO image file in the virtual machine settings

    </figcaption></figure>

<p>Save the settings and connect to the VM by double clicking on it and press “Start”. You should now be greeted by the OpenSUSE boot selection menu of the installation disk. Press “Installation”.</p>

<figure class=""><a href="hyperv3.png" class="image-popup" title="Figure 5: openSUSE boot menu
"><img src="hyperv3.png" alt="Screenshot of openSUSE boot menu" /></a><figcaption>
      Figure 5: openSUSE boot menu

    </figcaption></figure>

<p>(If you cannot get to this screen you might need to disable SecureBoot in the VM settings.)</p>

<p>And here’s another installation wizard. Some suggestions:</p>

<ul>
  <li>Network settings
    <ul>
      <li>I suggest skipping these for now. If you are using the NATSwitch the network should not be connected at this point anyway. When I tried configuring the network here it was only saved within the installer and not in the OS itself afterwards.</li>
    </ul>
  </li>
  <li>User Interface
    <ul>
      <li>This is a server so pick “Server”.</li>
    </ul>
  </li>
  <li>Partitioning
    <ul>
      <li>Press “Guided Setup”</li>
      <li>Do not enable LVM or encryption</li>
      <li>Change the file system type to EXT4, disable separate partitions for everything.</li>
    </ul>
  </li>
</ul>

<figure class=""><a href="suse1.png" class="image-popup" title="Figure 6: File system configuration
"><img src="suse1.png" alt="Screenshot of file system configuration" /></a><figcaption>
      Figure 6: File system configuration

    </figcaption></figure>

<ul>
  <li>Local User
    <ul>
      <li>I always add a user named “admin” so that there’s a separate one from root</li>
    </ul>
  </li>
  <li>Installation Settings
    <ul>
      <li>Disable the firewall</li>
      <li>Enable SSH service</li>
    </ul>
  </li>
</ul>

<figure class=""><a href="suse2.png" class="image-popup" title="Figure 7: SSH Service and Firewall
"><img src="suse2.png" alt="Screenshot of SSH service and firewall configuration" /></a><figcaption>
      Figure 7: SSH Service and Firewall

    </figcaption></figure>

<p>After the installation the VM should automatically reboot and show you the login screen. Login with the user you created and proceed having lots of fun, I guess.</p>

<figure class=""><a href="suse3.png" class="image-popup" title="Figure 8: Logon screen of openSUSE
"><img src="suse3.png" alt="Screenshot of the openSUSE logon screen" /></a><figcaption>
      Figure 8: Logon screen of openSUSE

    </figcaption></figure>

<h1 id="preparing-the-virtual-machine">Preparing the virtual machine</h1>

<p>Before we can install the application server first we need to get the network setup. Go into the VM settings again and attach NATSwitch to the machine. Then open the CLI configuration interface of SUSE:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>admin@linux-z3ax:~&gt; <span class="nb">sudo </span>yast
</code></pre></div></div>

<p>(You can do all of this by manually editing several configuration files instead)</p>

<p>Navigate to System -&gt; Network Settings using the arrow keys and press enter.</p>

<figure class=""><a href="suse4.png" class="image-popup" title="Figure 9: YaST Control Center
"><img src="suse4.png" alt="Screenshot of YaST Control Center" /></a><figcaption>
      Figure 9: YaST Control Center

    </figcaption></figure>

<p>You will get a message that currently the network settings are not managed by YaST. Therefore change “Network Setup Method” from “NetworkManager Service” to “Wicked Service”. You can navigate through these dialogs using the TAB key.</p>

<figure class=""><a href="suse5.png" class="image-popup" title="Figure 10: Network Setup Method
"><img src="suse5.png" alt="Screenshot of Network Setup Method" /></a><figcaption>
      Figure 10: Network Setup Method

    </figcaption></figure>

<p>On the same page you may want to disable IPv6, so that it does not get in the way. Then go to “Overview” and select “Edit” on “Virtual Ethernet Card 0”. Ensure that DHCP is not enabled and statically assign the IP address you chose and the subnet mask you configured in the NATSwitch. You should add the fixed hostname “vhcalnplci”. For me it looks like this:</p>

<figure class=""><a href="suse6.png" class="image-popup" title="Figure 11: Virtual Ethernet Card settings
"><img src="suse6.png" alt="Screenshot of virtual ethernet card settings" /></a><figcaption>
      Figure 11: Virtual Ethernet Card settings

    </figcaption></figure>

<p>Press next. In the “Hostname/DNS” tab you need to specify the new hostname “vhcalnplci” and you should also add some DNS servers of your choosing. For me these are the Google ones and the one of my home network.</p>

<figure class=""><a href="suse7.png" class="image-popup" title="Figure 12: Hostname and DNS
"><img src="suse7.png" alt="Screenshot of hostname and DNS settings" /></a><figcaption>
      Figure 12: Hostname and DNS

    </figcaption></figure>

<p>Go to “Routing” and change the “Default IPv4 Gateway” to the gateway you configured on the NATSwitch (for me that’s 192.168.99.1) for device eth0.</p>

<figure class=""><a href="suse8.png" class="image-popup" title="Figure 13: Routing configuration
"><img src="suse8.png" alt="Screenshot of routing settings" /></a><figcaption>
      Figure 13: Routing configuration

    </figcaption></figure>

<p>Press OK and leave YaST. Restart the virtual machine.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>admin@linux-z3ax:~&gt; <span class="nb">sudo </span>reboot
</code></pre></div></div>

<p>Next time you login you should now see the hostname has changed in the prompt and with a bit of luck networking should work. You can check by pinging Google using the IP for their public DNS server and their normal URL to check DNS does also work.</p>

<figure class=""><a href="suse9.png" class="image-popup" title="Figure 14: Internet access check
"><img src="suse9.png" alt="Screenshot of command line interface with ping command" /></a><figcaption>
      Figure 14: Internet access check

    </figcaption></figure>

<p>You might want to ping a machine in your local network to see if that works as well (like the host machine).</p>

<figure class=""><a href="suse10.png" class="image-popup" title="Figure 15: Local network check
"><img src="suse10.png" alt="Screenshot of command line interface with ping command" /></a><figcaption>
      Figure 15: Local network check

    </figcaption></figure>

<p>If everything’s fine I suggest creating a snapshot (or checkpoint as Hyper-V calls them).</p>

<p>If it doesn’t work you have some network troubleshooting to do. For me I either had to restart the machine again or I messed up some of the settings mentioned above. You might also need to configure a proxy server if that is relevant in your network environment.</p>

<p>The ABAP server needs a service called UUIDD (UUID Daemon). Since the internet connection should now work you can install and enable it like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>admin@vhcalnplci:~&gt; <span class="nb">sudo </span>zypper <span class="nb">install </span>uuidd
admin@vhcalnplci:~&gt; <span class="nb">sudo </span>systemctl <span class="nb">enable </span>uuidd
admin@vhcalnplci:~&gt; <span class="nb">sudo </span>systemctl start uuidd
</code></pre></div></div>

<p>I install some other packages as well. If you plan on extracting the installation files on the virtual machine you need some rar-compatible unarchiving software like unar. Alternatively you can extract the files on your host machine and just copy the result into the VM.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>admin@vhcalnplci:~&gt; <span class="nb">sudo </span>zypper <span class="nb">install </span>htop tmux unar
</code></pre></div></div>

<h1 id="installing-the-application-server">Installing the application server</h1>

<p>To install the AS ABAP server you first need to download the installation files from <a href="https://tools.hana.ondemand.com/#abap">https://tools.hana.ondemand.com/#abap</a>. Currently 7.51 SP02 is the most recent one (7.52 should be coming soon). After you downloaded the 10 rar-archive-files you need to transfer them inside of the virtual machine (you could instead directly install it over the network but I had issues with that). There are several ways to do this.</p>

<p>If you already have some network share available you can save the rar-files in there and mount the folder in the virtual machine and copy them over in a temporary directory:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>admin@vhcalnplci:~&gt; <span class="nb">sudo mkdir</span> <span class="nt">-p</span> /mnt/share
admin@vhcalnplci:~&gt; <span class="nb">sudo </span>mount.cifs <span class="s2">"//fileserver/share"</span> /mnt/share <span class="nt">-o</span> <span class="nv">user</span><span class="o">=</span>YourUser
admin@vhcalnplci:~&gt; <span class="nb">mkdir</span> <span class="nt">-p</span> /tmp/asabap
admin@vhcalnplci:~&gt; <span class="nb">cp</span> /mnt/share/sap<span class="k">*</span>.rar /tmp/asabap
</code></pre></div></div>

<p>If that’s not the case you could share the folder with the downloaded files on your local machine and mount that. I do not know of a way to just attach a folder of the host machine without network sharing like you can do in VirtualBox.</p>

<p>To extract the files I use unar that I installed previously:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>admin@vhcalnplci:~&gt; <span class="nb">cd</span> /tmp/asabap
admin@vhcalnplci:/tmp/asabap&gt; unar sap_netweaver_as_abap_751_sp02_ase_dev_edition.part01.rar
</code></pre></div></div>

<p>Remove the rar files to get some disk space back and set the installer to executable:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>admin@vhcalnplci:/tmp/asabap&gt; <span class="nb">rm</span> <span class="k">*</span>.rar
admin@vhcalnplci:/tmp/asabap&gt; <span class="nb">cd </span>sap_netweaver_as_abap_751_sp02_ase_dev_edition
admin@vhcalnplci:/tmp/asabap/sap_netweaver_as_abap_751_sp02_ase_dev_edition&gt; <span class="nb">chmod</span> +x install.sh
</code></pre></div></div>

<p>Create a new checkpoint here.</p>

<h2 id="ase-license">ASE License</h2>

<p>If you are installing the AS ABAP 7.51 SP02 you might now have a licensing problem. The ASE license packaged in the installation files has already expired. You can download the new valid license here, but somehow you need to swap out the license before the installer uses it. There are two ways to do this:</p>

<ol>
  <li>Extract some more archives, swap out the file and compress them again (guide in this comment)</li>
  <li>Swap it out while the installation is going on before the license is used but after it was extracted to the target folder</li>
</ol>

<p>I’ll describe the second approach as the other one is already documented and I cannot be bothered to remember any tar command syntax.</p>

<h2 id="start-the-installation">Start the installation</h2>

<p>Just execute the installer with sudo and follow the instructions:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>admin@vhcalnplci:/tmp/asabap/sap...&gt; <span class="nb">sudo</span> ./install.sh <span class="nt">-s</span>
</code></pre></div></div>

<p>(For some reason without the -s flag the installer will complain about the hostname. It should however be set up correctly and everything works fine skipping the check, so I am not sure why it is doing that.)</p>

<p>If you haven’t fixed the license yet follow the instructions until the installation is running. Then switch to another TTY session by pressing Ctrl+Alt+F2. You should now be on a new login screen (you can switch back with Ctrl+Alt+F1). Login, switch to root and wait for this file to appear:</p>

<p><code class="language-plaintext highlighter-rouge">/sybase/NPL/SYSAM‐2_0/licenses/SYBASE_ASE_TestDrive.lic</code></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>admin@vhcalnplci:~&gt; <span class="nb">sudo </span>su
vhcalnplci:/home/admin <span class="c"># cd /sybase/NPL</span>
vhcalnplci:/sybase/NPL <span class="c"># l</span>
vhcalnplci:/sybase/NPL <span class="c"># cp /mnt/share/SYBASE_ASE_TestDrive.lic /sybase/NPL/SYSAM-2_0/licenses/SYBASE_ASE_TestDrive.lic</span>
vhcalnplci:/sybase/NPL <span class="c"># cd /sybase/NPL/SYSAM-2_0/licenses</span>
vhcalnplci:/sybase/NPL/SYSAM-2_0/licenses <span class="c"># chmod 0750 SYBASE_ASE_TestDrive.lic</span>
vhcalnplci:/sybase/NPL/SYSAM-2_0/licenses <span class="c"># chown sybnpl:sapsys SYBASE_ASE_TestDrive.lic</span>
vhcalnplci:/sybase/NPL/SYSAM-2_0/licenses <span class="c"># cat SYBASE_ASE_TestDrive.lic</span>
vhcalnplci:/sybase/NPL/SYSAM-2_0/licenses <span class="c"># exit</span>
admin@vhcalnplci:~&gt; <span class="nb">exit</span>
</code></pre></div></div>

<p>The easiest way is to copy the file over from the mount point where your installation files were. With the cat command you should verify that the license is valid until some point in the future.</p>

<p>Switch back to the installation using Ctrl+Alt+F1 and wait until it’s done.</p>

<p>Hopefully you should now get this screen:</p>

<figure class=""><a href="suse11.png" class="image-popup" title="Figure 16: Installation successful
"><img src="suse11.png" alt="Screenshot of installation success screen" /></a><figcaption>
      Figure 16: Installation successful

    </figcaption></figure>

<p>This means to system was successfully installed and is up and running. You can connect to it with SAP GUI using the NAT-IP address (192.168.99.10 here). For ADT and other web-based things to work you should add it to your host file and use the hostname vhcalnplci instead.</p>

<p>For cleanup purposes clear the installation folder and unmount the network share:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>admin@vhcalnplci:~&gt; <span class="nb">sudo </span>umount /mnt/share
admin@vhcalnplci:~&gt; <span class="nb">rm</span> <span class="nt">-rf</span> /tmp/asabap
</code></pre></div></div>

<p>Create a new snapshot / checkpoint now just so you can return to this point if needed.</p>

<h1 id="next-steps">Next steps</h1>

<p>You now have an ABAP application server installed inside of a Hyper-V-managed virtual machine that uses a local NAT network and is accessible over SSH for administration convenience. Next you should install the trial license in transaction SLICENSE. For information on that and also on how to start / stop the system, users etc. please look at the more official blogs as these topics are not specific to Hyper-V:</p>

<p><a href="https://blogs.sap.com/2017/09/04/sap-as-abap-7.51-sp2-developer-edition-to-download-concise-installation-guide/">SAP AS ABAP 7.51 SP2 – Developer Edition to Download: Concise Installation Guide</a></p>

<p><a href="https://blogs.sap.com/2017/09/04/newbies-guide-installing-abap-as-751-sp02-on-linux/">Newbies Guide: Installing ABAP AS 751 SP02 on Linux</a></p>

<p><a href="https://blogs.sap.com/2017/09/14/sap-as-abap-7.51-developer-edition-faqs/">SAP AS ABAP 7.51 Developer Edition: FAQs</a></p>

<h1 id="why-should-i-use-hyper-v-though">Why should I use Hyper-V though?</h1>

<p>This all may seem like a lot of effort compared to using VirtualBox. If you are content with VirtualBox then please feel free to use it. This guide is just for people who already want to or have to use Hyper-V. For example if you are using Docker on Windows it internally uses Hyper-V by default. This makes VirtualBox unusable like I mentioned in the beginning, so that is a use case. In my opinion it is also kind of nice to have the virtualization software behave more like a background service instead of a desktop application.</p>]]></content><author><name>Fabian Lupa</name></author><category term="developer tools" /><category term="developer edition" /><category term="Hyper-V" /><category term="trial" /><summary type="html"><![CDATA[If you want to install the AS ABAP Developer Edition, one choice you have to make is picking the virtualization software that manages the virtual machine. Common options are...]]></summary></entry><entry><title type="html">Enumerations in ABAP</title><link href="https://lupa.dev/2017/08/15/enumerations-in-abap/" rel="alternate" type="text/html" title="Enumerations in ABAP" /><published>2017-08-15T00:00:00+00:00</published><updated>2017-08-15T00:00:00+00:00</updated><id>https://lupa.dev/2017/08/15/enumerations-in-abap</id><content type="html" xml:base="https://lupa.dev/2017/08/15/enumerations-in-abap/"><![CDATA[<p>To represent a fixed value set in ABAP you can use several different technologies. The newest one is <a href="https://blogs.sap.com/2016/10/10/abap-news-release-7.51-enumerations/">enumerations</a> which are provided at language level and can be used as of AS ABAP 7.51. With this blog post I want to show which possibilities there are for ABAP developers to define enumerations and use them in signature elements in the safest way possible.</p>

<h1 id="goals-of-enumerations">Goals of enumerations</h1>

<p>Enumerations enable you to define a fixed amount of named values for a specific context within the type system of a programming language. These behave like constants but have the additional feature of being limited to the defined values (and not to the type of the constant). This is especially useful when defining formal parameters using enumerations, because then the user of your API is less likely to use your method wrong (because the type system does not allow it, it would be a compile time error).</p>

<h1 id="example-of-api-miss-usage">Example of API miss-usage</h1>

<p>Think of the following example:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CLASS</span> <span class="n">lcl_light_switcher</span> <span class="k">DEFINITION</span><span class="p">.</span>
  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">CLASS</span><span class="o">-</span><span class="k">METHODS</span><span class="p">:</span>
      <span class="c1">"! Switch the lights on / off</span>
      <span class="c1">"! @parameter iv_on | Should they be on?</span>
      <span class="k">switch</span> <span class="k">IMPORTING</span> <span class="n">iv_on</span> <span class="k">TYPE</span> <span class="n">abap_bool</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>

<p>For most of us it is clear that <code class="language-plaintext highlighter-rouge">iv_on</code> should either be <code class="language-plaintext highlighter-rouge">' '</code> or <code class="language-plaintext highlighter-rouge">'X'</code> because the type of the parameter is <code class="language-plaintext highlighter-rouge">abap_bool</code>. So typical usage would be something like this (preferred using the abap_* constants):</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">lcl_light_switcher</span><span class="o">=&gt;</span><span class="n">switch</span><span class="p">(</span> <span class="n">abap_true</span> <span class="p">).</span>
<span class="n">lcl_light_switcher</span><span class="o">=&gt;</span><span class="n">switch</span><span class="p">(</span> <span class="n">abap_false</span> <span class="p">).</span>
<span class="n">lcl_light_switcher</span><span class="o">=&gt;</span><span class="n">switch</span><span class="p">(</span> <span class="s1">' '</span> <span class="p">).</span>
<span class="n">lcl_light_switcher</span><span class="o">=&gt;</span><span class="n">switch</span><span class="p">(</span> <span class="k">space</span> <span class="p">).</span>
<span class="n">lcl_light_switcher</span><span class="o">=&gt;</span><span class="n">switch</span><span class="p">(</span> <span class="s1">'X'</span> <span class="p">).</span>
</code></pre></div></div>

<p>However, the type system does not force you to use <code class="language-plaintext highlighter-rouge">' '</code> or <code class="language-plaintext highlighter-rouge">'X'</code> because <code class="language-plaintext highlighter-rouge">abap_bool</code> is just a type definition in the global type-pool abap and its actual type is C with a length of 1.</p>

<figure class=""><a href="type-pool-abap.png" class="image-popup" title="Figure 1: Type Pool ABAP
"><img src="type-pool-abap.png" alt="Screenshot of type pool ABAP" /></a><figcaption>
      Figure 1: Type Pool ABAP

    </figcaption></figure>

<p>This means the following does not lead to a compiler error:</p>
<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">lcl_light_switcher</span><span class="o">=&gt;</span><span class="n">switch</span><span class="p">(</span> <span class="s1">'A'</span> <span class="p">).</span>
</code></pre></div></div>

<p>It will however probably lead to an error in the implementation of the method because the programmer who implemented it does not expect someone to use <code class="language-plaintext highlighter-rouge">'A'</code> as the actual parameter because <em>by convention</em> only <code class="language-plaintext highlighter-rouge">' '</code> and <code class="language-plaintext highlighter-rouge">'X'</code> should be used. For abap_bool this is probably fine because most developers know about abap’s missing in-build boolean type and the ways around it. But what about other types that are module / application specific or that you define on your own?</p>

<h1 id="ways-to-define-fixed-value-quantities-in-abap">Ways to define fixed value quantities in ABAP</h1>

<p>Let’s say we have more states than just true and false. Our lamp is not just on or off now but can display different colors (like traffic lights).</p>

<h2 id="enumeration-values-as-constants">Enumeration values as constants</h2>

<p>If we want to use constants now (like <code class="language-plaintext highlighter-rouge">abap_true</code> and <code class="language-plaintext highlighter-rouge">abap_false</code>) we have to define them on our own. But where do we do that? There are three different types of development objects to use:</p>

<ul>
  <li>TYPE-POOLS: like SLIS, OSCON, ABAP, …</li>
  <li>INTERFACES: like IF_SALV_C_*, generated interfaces in BOPF, …</li>
  <li>CLASSES: either directly in the class with the method that needs the constant or in its own, like CL_GUI_CONTROL=&gt;METRIC_PIXEL</li>
</ul>

<p>Since type pools are obsolete, interfaces (or also classes) are the way to go nowadays. Here is an example using a constants-interface:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">INTERFACE</span> <span class="n">zif_light_states</span> <span class="k">PUBLIC</span><span class="p">.</span>
  <span class="k">TYPES</span><span class="p">:</span>
    <span class="n">gty_light_state</span> <span class="k">TYPE</span> <span class="k">i</span><span class="p">.</span>
  <span class="k">CONSTANTS</span><span class="p">:</span>
    <span class="n">gc_green</span>          <span class="k">TYPE</span> <span class="n">gty_light_state</span> <span class="k">VALUE</span> <span class="mi">0</span><span class="p">,</span>
    <span class="n">gc_yellow</span>         <span class="k">TYPE</span> <span class="n">gty_light_state</span> <span class="k">VALUE</span> <span class="mi">1</span><span class="p">,</span>
    <span class="n">gc_red</span>            <span class="k">TYPE</span> <span class="n">gty_light_state</span> <span class="k">VALUE</span> <span class="mi">2</span><span class="p">,</span>
    <span class="n">gc_red_and_yellow</span> <span class="k">TYPE</span> <span class="n">gty_light_state</span> <span class="k">VALUE</span> <span class="mi">3</span><span class="p">.</span>
<span class="k">ENDINTERFACE</span><span class="p">.</span>

<span class="k">CLASS</span> <span class="n">zcl_light_switcher</span> <span class="k">DEFINITION</span>
  <span class="k">PUBLIC</span>
  <span class="k">CREATE</span> <span class="k">PUBLIC</span><span class="p">.</span>

  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">METHODS</span><span class="p">:</span>
      <span class="c1">"! Switch the light</span>
      <span class="c1">"! @parameter iv_new_state | The new light color (see ZIF_LIGHT_STATES constants)</span>
      <span class="c1">"! @raising zcx_illegal_argument | iv_new_state is invalid</span>
      <span class="n">switch_light</span> <span class="k">IMPORTING</span> <span class="n">iv_new_state</span> <span class="k">TYPE</span> <span class="n">zif_light_states</span><span class="o">=&gt;</span><span class="n">gty_light_state</span>
                   <span class="k">RAISING</span>   <span class="n">zcx_illegal_argument</span><span class="p">.</span>
  <span class="k">PROTECTED</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">DATA</span><span class="p">:</span>
      <span class="n">mv_light</span> <span class="k">TYPE</span> <span class="n">zif_light_states</span><span class="o">=&gt;</span><span class="n">gty_light_state</span><span class="p">.</span>
  <span class="k">PRIVATE</span> <span class="k">SECTION</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>

<span class="k">CLASS</span> <span class="n">zcl_light_switcher</span> <span class="k">IMPLEMENTATION</span><span class="p">.</span>
  <span class="k">METHOD</span> <span class="n">switch_light</span><span class="p">.</span>
    <span class="k">TYPES</span><span class="p">:</span> <span class="n">lty_i_range</span> <span class="k">TYPE</span> <span class="k">RANGE</span> <span class="k">OF</span> <span class="n">zif_light_states</span><span class="o">=&gt;</span><span class="n">gty_light_state</span><span class="p">.</span>

    <span class="c1">" Validate iv_new_state</span>
    <span class="k">IF</span> <span class="n">iv_new_state</span> <span class="k">NOT</span> <span class="k">IN</span> <span class="k">VALUE</span> <span class="n">lty_i_range</span><span class="p">(</span>
          <span class="p">(</span> <span class="k">sign</span> <span class="o">=</span> <span class="s1">'I'</span> <span class="k">option</span> <span class="o">=</span> <span class="s1">'EQ'</span> <span class="k">low</span> <span class="o">=</span> <span class="n">zif_light_states</span><span class="o">=&gt;</span><span class="n">gc_green</span> <span class="p">)</span>
          <span class="p">(</span> <span class="k">sign</span> <span class="o">=</span> <span class="s1">'I'</span> <span class="k">option</span> <span class="o">=</span> <span class="s1">'EQ'</span> <span class="k">low</span> <span class="o">=</span> <span class="n">zif_light_states</span><span class="o">=&gt;</span><span class="n">gc_yellow</span> <span class="p">)</span>
          <span class="p">(</span> <span class="k">sign</span> <span class="o">=</span> <span class="s1">'I'</span> <span class="k">option</span> <span class="o">=</span> <span class="s1">'EQ'</span> <span class="k">low</span> <span class="o">=</span> <span class="n">zif_light_states</span><span class="o">=&gt;</span><span class="n">gc_red</span> <span class="p">)</span>
          <span class="p">(</span> <span class="k">sign</span> <span class="o">=</span> <span class="s1">'I'</span> <span class="k">option</span> <span class="o">=</span> <span class="s1">'EQ'</span> <span class="k">low</span> <span class="o">=</span> <span class="n">zif_light_states</span><span class="o">=&gt;</span><span class="n">gc_red_and_yellow</span> <span class="p">)</span>
        <span class="p">).</span>
      <span class="k">RAISE</span> <span class="k">EXCEPTION</span> <span class="k">TYPE</span> <span class="n">zcx_illegal_argument</span>
        <span class="k">EXPORTING</span>
          <span class="n">is_message</span>  <span class="o">=</span> <span class="n">zcx_illegal_argument</span><span class="o">=&gt;</span><span class="n">gc_with_name_and_value</span>
          <span class="n">iv_par_name</span> <span class="o">=</span> <span class="s1">'IV_NEW_STATE'</span>
          <span class="n">iv_value</span>    <span class="o">=</span> <span class="n">iv_new_state</span> <span class="o">##</span><span class="n">NO_TEXT</span><span class="p">.</span>
    <span class="k">ENDIF</span><span class="p">.</span>

    <span class="n">mv_light</span> <span class="o">=</span> <span class="n">iv_new_state</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>

<p>As you can see the user of the API is pointed to the constants interface in the method documentation and by the type of the importing parameter. The type declaration of <code class="language-plaintext highlighter-rouge">gty_light_state</code> in the interface would normally be unnecessary (because the inbuilt type I of course already exists) but it helps to reference the place where the constants are stored. You could of course also create a DDIC data element and refer to the interface in its description. But if it is not documented somewhere, the API user is forced to look into the implementation of the method to find out what actual parameters to use (which is troublesome and not always possible, think of defining your method in an interface without implementation).</p>

<p>So now the possible values are known, but still, as the developer implementing the method (and using design by contract principle) you should validate the actual parameters of the caller. Which means checking if it is one of the constants. In the example above this is done using a range table. You can see another weak point here. If one decides to add another state to our traffic lights (like blinking yellow) the validation logic would have to be updated even if the rest of the method could handle an additional state just fine.</p>

<p>One advantage or disadvantage of using interfaces for constant definitions is that you can implement the interface in the class that uses them. This enables you to access the constants directly (as long as you defined aliases). On the other hand you will also find everything the interface defines in the debugger if you inspect an object instance in the members view. I am personally not a fan of doing that.</p>

<h2 id="ddic-domain-values">DDIC Domain values</h2>

<p>Another way is using domains in the data dictionary. These seem particularly well suited for this task at first.</p>

<figure class=""><a href="domain-fixed-values.png" class="image-popup" title="Figure 2: Fixed Values in a Domain
"><img src="domain-fixed-values.png" alt="Screenshot of fixed values in domain" /></a><figcaption>
      Figure 2: Fixed Values in a Domain

    </figcaption></figure>

<p>You can easily validate if a variable’s value is defined in a domain using standard function modules or RTTI. Here is the same light switcher class using a domain (/ a data element that refers to a domain):</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CLASS</span> <span class="n">zcl_light_switcher</span> <span class="k">DEFINITION</span>
  <span class="k">PUBLIC</span>
  <span class="k">CREATE</span> <span class="k">PUBLIC</span><span class="p">.</span>

  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">METHODS</span><span class="p">:</span>
      <span class="c1">"! Switch the light</span>
      <span class="c1">"! @parameter iv_new_state | The new light color (see ZSCN_D_LIGHTSTATE domain)</span>
      <span class="c1">"! @raising zcx_illegal_argument | iv_new_state is invalid</span>
      <span class="n">switch_light</span> <span class="k">IMPORTING</span> <span class="n">iv_new_state</span> <span class="k">TYPE</span> <span class="n">zscn_l_lightstate</span>
                   <span class="k">RAISING</span>   <span class="n">zcx_illegal_argument</span><span class="p">.</span>
  <span class="k">PROTECTED</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">DATA</span><span class="p">:</span>
      <span class="n">mv_light</span> <span class="k">TYPE</span> <span class="n">zscn_l_lightstate</span><span class="p">.</span>
  <span class="k">PRIVATE</span> <span class="k">SECTION</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>

<span class="k">CLASS</span> <span class="n">zcl_light_switcher</span> <span class="k">IMPLEMENTATION</span><span class="p">.</span>
  <span class="k">METHOD</span> <span class="n">switch_light</span><span class="p">.</span>
    <span class="c1">" Validate iv_new_state</span>
    <span class="k">CALL</span> <span class="k">FUNCTION</span> <span class="s1">'CHECK_DOMAIN_VALUES'</span>
      <span class="k">EXPORTING</span>
        <span class="n">domname</span> <span class="o">=</span> <span class="s1">'ZSCN_D_LIGHTSTATE'</span>
        <span class="k">value</span>   <span class="o">=</span> <span class="n">iv_new_state</span><span class="p">.</span>

    <span class="k">IF</span> <span class="n">sy</span><span class="o">-</span><span class="n">subrc</span> <span class="o">&lt;&gt;</span> <span class="mi">0</span><span class="p">.</span>
      <span class="k">RAISE</span> <span class="k">EXCEPTION</span> <span class="k">TYPE</span> <span class="n">zcx_illegal_argument</span>
        <span class="k">EXPORTING</span>
          <span class="n">is_message</span>  <span class="o">=</span> <span class="n">zcx_illegal_argument</span><span class="o">=&gt;</span><span class="n">gc_not_in_domain</span>
          <span class="n">iv_par_name</span> <span class="o">=</span> <span class="s1">'IV_NEW_STATE'</span>
          <span class="n">iv_value</span>    <span class="o">=</span> <span class="n">iv_new_state</span>
          <span class="n">iv_domname</span>  <span class="o">=</span> <span class="s1">'ZSCN_D_LIGHTSTATE'</span> <span class="o">##</span><span class="n">NO_TEXT</span><span class="p">.</span>
    <span class="k">ENDIF</span><span class="p">.</span>

    <span class="n">mv_light</span> <span class="o">=</span> <span class="n">iv_new_state</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>

<p>However, one thing to keep in mind is that there is no way to refer to each of the values in a static way (as they have to be fetched from the database first). That sometimes leads to a situation where you have a domain and constants you have to keep in sync. For example in a CASE statement. An advantage is though, that you can use traditional dynpro value helps / you get them “for free” with localization support (and also domain appends).</p>

<h2 id="using-new-751-enumerations">Using new 7.51 enumerations</h2>

<p>Since AS ABAP 7.51 there is a construct available at language level for exactly the use case described here. The implementation chosen by SAP (to my surprise) is “value type based” and not object oriented as in many other languages (like Java or C#). In short: by defining an enumeration you define a type (value type to be exact) and associated constants for it, that implicitely get values assigned if you don’t do it yourself. Additionally any assignment to data objects that are typed as an enumeration type will get compile time checks if the associated constants are used or not. If not it results in a syntax error.</p>

<p>I unfortunately do not have access to a 7.51 system (AS ABAP 7.51 developer edition where are you?) but looking at the documentation it should work like this:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CLASS</span> <span class="n">zcl_light_switcher</span> <span class="k">DEFINITION</span>
  <span class="k">PUBLIC</span>
  <span class="k">CREATE</span> <span class="k">PUBLIC</span><span class="p">.</span>

  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">TYPES</span><span class="p">:</span>
      <span class="k">BEGIN</span> <span class="k">OF</span> <span class="n">ENUM</span> <span class="n">gte_light_state</span><span class="p">,</span>
        <span class="k">green</span><span class="p">,</span>
        <span class="k">yellow</span><span class="p">,</span>
        <span class="k">red</span><span class="p">,</span>
        <span class="n">red_and_yellow</span><span class="p">,</span>
      <span class="k">END</span> <span class="k">OF</span> <span class="n">ENUM</span> <span class="n">gte_light_state</span><span class="p">.</span>
    <span class="k">METHODS</span><span class="p">:</span>
      <span class="n">switch_light</span> <span class="k">IMPORTING</span> <span class="n">ie_new_state</span> <span class="k">TYPE</span> <span class="n">gte_light_state</span><span class="p">.</span>
<span class="p">(...)</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>

<p>In my opinion these should replace the interface constants in all use cases, as long as you don’t have to worry about backwards compatibility.</p>

<p>I think it makes sense in ABAP to not have enumerations implemented as classes because of the lack of inner classes (local classes are not statically accessable outside of the class). So you would have to create a global class for any small enumeration, which, if you do it consistently, quickly assumes alarming proportions like the SALV  library has with constant interfaces.</p>

<p>However in other cases you might want to bundle some functionality with your enumeration instead of just providing the fixed values. Let’s see how you can do that.</p>

<h2 id="class-based-enumerations">Class based enumerations</h2>

<p>In other programming languages with OO support (like Java or C#) there are so called enumeration classes. Unlike the new ABAP language construct which is also called enumeration, enumeration classes are “syntax sugar” that generate classes and instances of them for each fixed value. In Java for example you can write something like this for our traffic lights:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">enum</span> <span class="nc">LightStates</span> <span class="o">{</span>
    <span class="no">GREEN</span><span class="o">,</span> <span class="no">YELLOW</span><span class="o">,</span> <span class="no">RED</span><span class="o">,</span> <span class="no">RED_AND_YELLOW</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<p>What happens behind the scenes is that a normal Java class is generated with public static final class attributes for each of the fixed values. These are reference variables to their own class LightStates which are initialized in the static class constructor. Since these are just normal Java classes under the hood you can add additional methods, attributes etc. <em>(please don’t quote me on how exactly the compiler does it)</em></p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">enum</span> <span class="nc">LightStates</span> <span class="o">{</span>
    <span class="no">GREEN</span><span class="o">(</span><span class="s">"Green"</span><span class="o">),</span>
    <span class="no">YELLOW</span><span class="o">(</span><span class="s">"Yellow"</span><span class="o">),</span>
    <span class="no">RED</span><span class="o">(</span><span class="s">"Red"</span><span class="o">),</span>
    <span class="no">RED_AND_YELLOW</span><span class="o">(</span><span class="s">"Red and yellow"</span><span class="o">);</span>

    <span class="kd">private</span> <span class="nc">String</span> <span class="n">name</span><span class="o">;</span>

    <span class="kd">private</span> <span class="nf">LightStates</span><span class="o">(</span><span class="nc">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">this</span><span class="o">.</span><span class="na">name</span> <span class="o">=</span> <span class="n">name</span><span class="o">;</span>
    <span class="o">}</span>

    <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getName</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="n">name</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>In ABAP we unfortunately do not have enumeration classes. We can however implement them “manually” effectively skipping the step the Java compiler does automatically.</p>

<h2 id="minimal-oo-enum-implementation">Minimal OO enum implementation</h2>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CLASS</span> <span class="n">zcl_light_state</span> <span class="k">DEFINITION</span>
  <span class="k">PUBLIC</span>
  <span class="k">FINAL</span>
  <span class="k">CREATE</span> <span class="k">PRIVATE</span><span class="p">.</span>

  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">CLASS</span><span class="o">-</span><span class="k">METHODS</span><span class="p">:</span>
      <span class="n">class_constructor</span><span class="p">.</span>
    <span class="k">CLASS</span><span class="o">-</span><span class="k">DATA</span><span class="p">:</span>
      <span class="n">go_green</span>          <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span> <span class="k">READ</span><span class="o">-</span><span class="k">ONLY</span><span class="p">,</span>
      <span class="n">go_yellow</span>         <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span> <span class="k">READ</span><span class="o">-</span><span class="k">ONLY</span><span class="p">,</span>
      <span class="n">go_red</span>            <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span> <span class="k">READ</span><span class="o">-</span><span class="k">ONLY</span><span class="p">,</span>
      <span class="n">go_red_and_yellow</span> <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span> <span class="k">READ</span><span class="o">-</span><span class="k">ONLY</span><span class="p">.</span>
  <span class="k">PROTECTED</span> <span class="k">SECTION</span><span class="p">.</span>
  <span class="k">PRIVATE</span> <span class="k">SECTION</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>

<span class="k">CLASS</span> <span class="n">zcl_light_state</span> <span class="k">IMPLEMENTATION</span><span class="p">.</span>
  <span class="k">METHOD</span> <span class="n">class_constructor</span><span class="p">.</span>
    <span class="k">CREATE</span> <span class="k">OBJECT</span><span class="p">:</span> <span class="n">go_green</span><span class="p">,</span> <span class="n">go_yellow</span><span class="p">,</span> <span class="n">go_red</span><span class="p">,</span> <span class="n">go_red_and_yellow</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>

<p>As you can see just like in the Java enumeration we have static reference variables for each of the enumeration values. These cannot be changed from the outside (because they are <code class="language-plaintext highlighter-rouge">READ-ONLY</code> and effectively final). They are loaded on first use of the class using the class constructor and refer to instances of the class itself. It is not possible to create any other instances of the class because the constructor is private (<code class="language-plaintext highlighter-rouge">CREATE PRIVATE</code>) and the class is also final (so no publicly creatable subclasses). This means there can only ever be these 4 instances of the class available <sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>. You can now use the enumeration class similarly to the constants interface but except for comparing constant values now memory addresses will be compared.</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CLASS</span> <span class="n">zcl_light_switcher</span> <span class="k">DEFINITION</span>
  <span class="k">PUBLIC</span>
  <span class="k">CREATE</span> <span class="k">PUBLIC</span><span class="p">.</span>

  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">METHODS</span><span class="p">:</span>
      <span class="c1">"! Switch the light</span>
      <span class="c1">"! @parameter io_new_state | The new light color</span>
      <span class="c1">"! @raising zcx_illegal_argument | io_new_state cannot be null</span>
      <span class="n">switch_light</span> <span class="k">IMPORTING</span> <span class="n">io_new_state</span> <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span>
                   <span class="k">RAISING</span>   <span class="n">zcx_illegal_argument</span><span class="p">,</span>
      <span class="c1">"! @parameter ro_state | Current light state</span>
      <span class="n">get_current_state</span> <span class="k">RETURNING</span> <span class="nb">VALUE</span><span class="p">(</span><span class="n">ro_state</span><span class="p">)</span> <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span><span class="p">.</span>
  <span class="k">PROTECTED</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">DATA</span><span class="p">:</span>
      <span class="n">mo_light</span> <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span><span class="p">.</span>
  <span class="k">PRIVATE</span> <span class="k">SECTION</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>

<span class="k">CLASS</span> <span class="n">zcl_light_switcher</span> <span class="k">IMPLEMENTATION</span><span class="p">.</span>
  <span class="k">METHOD</span> <span class="n">switch_light</span><span class="p">.</span>
    <span class="c1">" Validate io_new_state</span>
    <span class="k">IF</span> <span class="n">io_new_state</span> <span class="k">IS</span> <span class="k">NOT</span> <span class="k">BOUND</span><span class="p">.</span>
      <span class="k">RAISE</span> <span class="k">EXCEPTION</span> <span class="k">TYPE</span> <span class="n">zcx_illegal_argument</span>
        <span class="k">EXPORTING</span>
          <span class="n">is_message</span>  <span class="o">=</span> <span class="n">zcx_illegal_argument</span><span class="o">=&gt;</span><span class="n">gc_nullpointer</span>
          <span class="n">iv_par_name</span> <span class="o">=</span> <span class="s1">'IO_NEW_STATE'</span> <span class="o">##</span><span class="n">NO_TEXT</span><span class="p">.</span>
    <span class="k">ENDIF</span><span class="p">.</span>

    <span class="n">mo_light</span> <span class="o">=</span> <span class="n">io_new_state</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>

  <span class="k">METHOD</span> <span class="n">get_current_state</span><span class="p">.</span>
    <span class="n">ro_state</span> <span class="o">=</span> <span class="n">mo_light</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>

<p>For the caller of the <code class="language-plaintext highlighter-rouge">switch_light</code> method it is clear that <code class="language-plaintext highlighter-rouge">io_new_state</code> must be an instance of zcl_light_state because of the type of the parameter. He can therefore (knowing of class based enumerations) look for the available values in the class-data of the class and use one of the values as the actual parameter, like this:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">lo_light_switcher</span><span class="o">-&gt;</span><span class="n">switch_light</span><span class="p">(</span> <span class="n">zcl_light_states</span><span class="o">=&gt;</span><span class="n">go_green</span> <span class="p">).</span>
</code></pre></div></div>

<p>Because the parameter is now an object reference to an instance of a class where there can only ever be the instances created which are defined in itself, the only illegal parameter value is null, which you should probably check for.
Compared to a constants interface or a domain or even 7.51 enumerations you might say this is much more effort. And it is. However, the advantages are that you let the type system check values of parameters at compile time (except null) and you have a full on ABAP objects class, in which you can bundle related functionality to the enumeration, like serialization, additional attributes etc. Here is a more advanced example.</p>

<h2 id="advanced-oo-enum-example">Advanced OO enum example</h2>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">"! Enumeration to represent states of traffic lights</span>
<span class="k">CLASS</span> <span class="n">zcl_light_state</span> <span class="k">DEFINITION</span>
  <span class="k">PUBLIC</span>
  <span class="k">FINAL</span>
  <span class="k">CREATE</span> <span class="k">PRIVATE</span><span class="p">.</span>

  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">CLASS</span><span class="o">-</span><span class="k">METHODS</span><span class="p">:</span>
      <span class="n">class_constructor</span><span class="p">,</span>
      <span class="c1">"! Get enum instance from name</span>
      <span class="c1">"! @parameter iv_name | Name of the enum value</span>
      <span class="c1">"! @parameter ro_light_state | Found enum value</span>
      <span class="c1">"! @raising zcx_illegal_argument | iv_name is not the name of any enum value</span>
      <span class="n">from_name</span> <span class="k">IMPORTING</span> <span class="n">iv_name</span>               <span class="k">TYPE</span> <span class="k">string</span>
                <span class="k">RETURNING</span> <span class="nb">VALUE</span><span class="p">(</span><span class="n">ro_light_state</span><span class="p">)</span> <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span>
                <span class="k">RAISING</span>   <span class="n">zcx_illegal_argument</span><span class="p">.</span>
    <span class="k">METHODS</span><span class="p">:</span>
      <span class="c1">"! Get the next state</span>
      <span class="c1">"! @parameter ro_next | Next state</span>
      <span class="n">get_next</span> <span class="k">RETURNING</span> <span class="nb">VALUE</span><span class="p">(</span><span class="n">ro_next</span><span class="p">)</span> <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span><span class="p">.</span>
    <span class="k">CLASS</span><span class="o">-</span><span class="k">DATA</span><span class="p">:</span>
      <span class="c1">"! Traffic can and should flow</span>
      <span class="n">go_green</span>          <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span> <span class="k">READ</span><span class="o">-</span><span class="k">ONLY</span><span class="p">,</span>
      <span class="c1">"! Traffic can flow but soon cannot</span>
      <span class="n">go_yellow</span>         <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span> <span class="k">READ</span><span class="o">-</span><span class="k">ONLY</span><span class="p">,</span>
      <span class="c1">"! Traffic must not flow</span>
      <span class="n">go_red</span>            <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span> <span class="k">READ</span><span class="o">-</span><span class="k">ONLY</span><span class="p">,</span>
      <span class="c1">"! Traffic must not flow but can soon</span>
      <span class="n">go_red_and_yellow</span> <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span> <span class="k">READ</span><span class="o">-</span><span class="k">ONLY</span><span class="p">.</span>
    <span class="k">DATA</span><span class="p">:</span>
      <span class="n">mv_name</span> <span class="k">TYPE</span> <span class="k">string</span> <span class="k">READ</span><span class="o">-</span><span class="k">ONLY</span><span class="p">.</span>
  <span class="k">PROTECTED</span> <span class="k">SECTION</span><span class="p">.</span>
  <span class="k">PRIVATE</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">METHODS</span><span class="p">:</span>
      <span class="c1">"! @parameter iv_name | Name of the light state, must be unique!</span>
      <span class="n">constructor</span> <span class="k">IMPORTING</span> <span class="n">iv_name</span> <span class="k">TYPE</span> <span class="kt">csequence</span><span class="p">.</span>
    <span class="k">CLASS</span><span class="o">-</span><span class="k">DATA</span><span class="p">:</span>
      <span class="n">gt_registry</span> <span class="k">TYPE</span> <span class="k">STANDARD</span> <span class="k">TABLE</span> <span class="k">OF</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>

<span class="k">CLASS</span> <span class="n">zcl_light_state</span> <span class="k">IMPLEMENTATION</span><span class="p">.</span>
  <span class="k">METHOD</span> <span class="n">class_constructor</span><span class="p">.</span>
    <span class="k">DEFINE</span> <span class="k">init</span><span class="p">.</span>
      <span class="o">&amp;</span><span class="mi">1</span> <span class="o">=</span> <span class="k">NEW</span> <span class="o">#</span><span class="p">(</span> <span class="o">&amp;</span><span class="mi">2</span> <span class="p">).</span>
      <span class="k">INSERT</span> <span class="o">&amp;</span><span class="mi">1</span> <span class="k">INTO</span> <span class="k">TABLE</span> <span class="n">gt_registry</span><span class="p">.</span>
    <span class="k">END</span><span class="o">-</span><span class="k">OF</span><span class="o">-</span><span class="k">DEFINITION</span><span class="p">.</span>

    <span class="k">init</span><span class="p">:</span> <span class="n">go_green</span>          <span class="s1">`Green`</span><span class="p">,</span>
          <span class="n">go_yellow</span>         <span class="s1">`Yellow`</span><span class="p">,</span>
          <span class="n">go_red</span>            <span class="s1">`Red`</span><span class="p">,</span>
          <span class="n">go_red_and_yellow</span> <span class="s1">`Red + Yellow`</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>

  <span class="k">METHOD</span> <span class="n">from_name</span><span class="p">.</span>
    <span class="k">TRY</span><span class="p">.</span>
        <span class="n">ro_light_state</span> <span class="o">=</span> <span class="n">gt_registry</span><span class="p">[</span> <span class="n">table_line</span><span class="o">-&gt;</span><span class="n">mv_name</span> <span class="o">=</span> <span class="n">iv_name</span> <span class="p">].</span>
      <span class="k">CATCH</span> <span class="n">cx_sy_itab_line_not_found</span> <span class="k">INTO</span> <span class="k">DATA</span><span class="p">(</span><span class="n">lx_ex</span><span class="p">).</span>
        <span class="k">RAISE</span> <span class="k">EXCEPTION</span> <span class="k">TYPE</span> <span class="n">zcx_illegal_argument</span>
          <span class="k">EXPORTING</span>
            <span class="n">is_message</span>  <span class="o">=</span> <span class="n">zcx_illegal_argument</span><span class="o">=&gt;</span><span class="n">gc_enum_not_registered</span>
            <span class="n">iv_par_name</span> <span class="o">=</span> <span class="s1">'IV_NAME'</span>
            <span class="n">iv_value</span>    <span class="o">=</span> <span class="n">iv_name</span>
            <span class="n">ix_previous</span> <span class="o">=</span> <span class="n">lx_ex</span> <span class="o">##</span><span class="n">NO_TEXT</span><span class="p">.</span>
    <span class="k">ENDTRY</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>

  <span class="k">METHOD</span> <span class="n">constructor</span><span class="p">.</span>
    <span class="n">mv_name</span> <span class="o">=</span> <span class="n">iv_name</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>

  <span class="k">METHOD</span> <span class="n">get_next</span><span class="p">.</span>
    <span class="n">ro_next</span> <span class="o">=</span> <span class="k">SWITCH</span> <span class="o">#</span><span class="p">(</span> <span class="n">me</span> <span class="k">WHEN</span> <span class="n">go_green</span>          <span class="k">THEN</span> <span class="n">go_yellow</span>
                           <span class="k">WHEN</span> <span class="n">go_yellow</span>         <span class="k">THEN</span> <span class="n">go_red</span>
                           <span class="k">WHEN</span> <span class="n">go_red</span>            <span class="k">THEN</span> <span class="n">go_red_and_yellow</span>
                           <span class="k">WHEN</span> <span class="n">go_red_and_yellow</span> <span class="k">THEN</span> <span class="n">go_green</span> <span class="p">).</span>
    <span class="c1">" If this fails a new enum value has been added and is not yet supported</span>
    <span class="c1">" by this method.</span>
    <span class="k">ASSERT</span> <span class="n">ro_next</span> <span class="k">IS</span> <span class="k">BOUND</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">lo_light_switcher</span><span class="o">-&gt;</span><span class="n">switch</span><span class="p">(</span> <span class="n">lo_light_switcher</span><span class="o">-&gt;</span><span class="n">get_current_state</span><span class="p">(</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">get_next</span><span class="p">(</span> <span class="p">)</span> <span class="p">).</span>
</code></pre></div></div>

<p>In standard SAP these classes are for example used in the ABAP workbench. If you look in the related development packages you can find classes prefixed with CE (E for enumeration I assume) that are built and used in exactly this way. In fact the workbench is a very good example for enumeration classes because they are ideally used in technical developments because persisting them to the database requires extra steps (they need to be serialized to a constant value again) and for classic user interfaces there will be no generated value help dialogs.</p>

<p>If you look further in the repository infosystem with CL<em>ENUM</em> you can find lots of other examples. There are even abstract base classes for enumerations you might want to take a look at (for example CL_BCFG_ENUM_BASE).</p>

<h1 id="comparison">Comparison</h1>

<table>
  <thead>
    <tr>
      <th>Technology</th>
      <th>Dynpro Support</th>
      <th>Static Access to Values</th>
      <th>Values Can Be Added Without Adjusting Validation Logic</th>
      <th>DB Persistence</th>
      <th>Enhancable Using Additional Methods / Attributes</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Constants (in Type-Pools, Interfaces, Classes)</td>
      <td>No</td>
      <td>Yes</td>
      <td>No</td>
      <td>Yes (using constant)</td>
      <td>No</td>
    </tr>
    <tr>
      <td>Domains</td>
      <td>Yes</td>
      <td>No</td>
      <td>Yes</td>
      <td>Yes (using domain key)</td>
      <td>No</td>
    </tr>
    <tr>
      <td>Enumerations (7.51)</td>
      <td>No</td>
      <td>Yes</td>
      <td>Yes (*check for INITIAL needed and maybe more? <sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>)</td>
      <td>Yes (using auto assigned constant)</td>
      <td>No</td>
    </tr>
    <tr>
      <td>OO Enumeration Classes</td>
      <td>No</td>
      <td>Yes</td>
      <td>Yes (only nullpointer validation needed)</td>
      <td>Serialization logic must be implemented</td>
      <td>Yes (full OO class)</td>
    </tr>
  </tbody>
</table>

<h1 id="combining-the-two-different-enumerations">Combining the two different enumerations</h1>

<p>So of course I thought “How can I have the advantages of both technologies, language level value type based enumerations with compile time checks and no nullpointers and also enumeration classes with utility methods and additional attributes?”.</p>

<p>So here’s a try at that, though I am not too happy with the following implementation. I don’t see a way to connect the enumeration constant with the enumeration class instance (“companion object”) without using a static accessor method.</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CLASS</span> <span class="n">zcl_light_state</span> <span class="k">DEFINITION</span>
  <span class="k">PUBLIC</span>
  <span class="k">FINAL</span>
  <span class="k">CREATE</span> <span class="k">PRIVATE</span><span class="p">.</span>

  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">TYPES</span><span class="p">:</span>
      <span class="k">BEGIN</span> <span class="k">OF</span> <span class="n">ENUM</span> <span class="n">gte_light_state</span><span class="p">,</span>
        <span class="k">green</span><span class="p">,</span>
        <span class="k">yellow</span><span class="p">,</span>
        <span class="k">red</span><span class="p">,</span>
        <span class="n">red_and_yellow</span><span class="p">,</span>
      <span class="k">END</span> <span class="k">OF</span> <span class="n">ENUM</span> <span class="n">gte_light_state</span><span class="p">.</span>
    <span class="k">CLASS</span><span class="o">-</span><span class="k">METHODS</span><span class="p">:</span>
      <span class="k">get</span> <span class="k">IMPORTING</span> <span class="n">ie_enum</span>               <span class="k">TYPE</span> <span class="n">gte_light_state</span>
          <span class="k">RETURNING</span> <span class="nb">VALUE</span><span class="p">(</span><span class="n">ro_light_state</span><span class="p">)</span> <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span><span class="p">.</span>
    <span class="k">METHODS</span><span class="p">:</span>
      <span class="n">get_next</span> <span class="k">RETURNING</span> <span class="nb">VALUE</span><span class="p">(</span><span class="n">ro_next</span><span class="p">)</span> <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span><span class="p">.</span>
    <span class="k">DATA</span><span class="p">:</span>
      <span class="n">mv_name</span> <span class="k">TYPE</span> <span class="k">string</span> <span class="k">READ</span><span class="o">-</span><span class="k">ONLY</span><span class="p">,</span>
      <span class="n">me_enum</span> <span class="k">TYPE</span> <span class="n">gte_light_state</span> <span class="k">READ</span><span class="o">-</span><span class="k">ONLY</span><span class="p">.</span>
  <span class="k">PROTECTED</span> <span class="k">SECTION</span><span class="p">.</span>
  <span class="k">PRIVATE</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">TYPES</span><span class="p">:</span>
      <span class="k">BEGIN</span> <span class="k">OF</span> <span class="n">gty_registry</span><span class="p">,</span>
        <span class="n">enum</span>     <span class="k">TYPE</span> <span class="n">gte_light_state</span><span class="p">,</span>
        <span class="k">instance</span> <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">zcl_light_state</span><span class="p">,</span>
      <span class="k">END</span> <span class="k">OF</span> <span class="n">gty_registry</span><span class="p">.</span>
    <span class="k">METHODS</span><span class="p">:</span>
      <span class="n">constructor</span> <span class="k">IMPORTING</span> <span class="n">iv_name</span> <span class="k">TYPE</span> <span class="kt">csequence</span>
                            <span class="n">ie_enum</span> <span class="k">TYPE</span> <span class="n">gte_light_state</span><span class="p">.</span>
    <span class="k">CLASS</span><span class="o">-</span><span class="k">DATA</span><span class="p">:</span>
      <span class="n">gt_registry</span> <span class="k">TYPE</span> <span class="k">SORTED</span> <span class="k">TABLE</span> <span class="k">OF</span> <span class="n">gty_registry</span> <span class="k">WITH</span> <span class="k">UNIQUE</span> <span class="k">KEY</span> <span class="n">enum</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>

<span class="k">CLASS</span> <span class="n">zcl_light_state</span> <span class="k">IMPLEMENTATION</span><span class="p">.</span>
  <span class="k">METHOD</span> <span class="n">class_constructor</span><span class="p">.</span>
    <span class="k">DEFINE</span> <span class="k">init</span><span class="p">.</span>
      <span class="k">NEW</span> <span class="o">#</span><span class="p">(</span> <span class="n">iv_name</span> <span class="o">=</span> <span class="o">&amp;</span><span class="mi">1</span> <span class="n">ie_enum</span> <span class="o">=</span> <span class="o">&amp;</span><span class="mi">2</span> <span class="p">).</span>
    <span class="k">END</span><span class="o">-</span><span class="k">OF</span><span class="o">-</span><span class="k">DEFINITION</span><span class="p">.</span>

    <span class="k">init</span><span class="p">:</span> <span class="s1">`Green`</span>        <span class="n">gte_light_state</span><span class="o">-</span><span class="k">green</span><span class="p">,</span>
          <span class="s1">`Yellow`</span>       <span class="n">gte_light_state</span><span class="o">-</span><span class="k">yellow</span><span class="p">,</span>
          <span class="s1">`Red`</span>          <span class="n">gte_light_state</span><span class="o">-</span><span class="k">red</span><span class="p">,</span>
          <span class="s1">`Red + Yellow`</span> <span class="n">gte_light_state</span><span class="o">-</span><span class="n">red_and_yellow</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>

  <span class="k">METHOD</span> <span class="k">get</span><span class="p">.</span>
    <span class="k">TRY</span><span class="p">.</span>
        <span class="n">ro_light_state</span> <span class="o">=</span> <span class="n">gt_registry</span><span class="p">[</span> <span class="n">enum</span> <span class="o">=</span> <span class="n">ie_enum</span> <span class="p">].</span>
      <span class="k">CATCH</span> <span class="n">cx_sy_itab_line_not_found</span> <span class="k">INTO</span> <span class="k">DATA</span><span class="p">(</span><span class="n">lx_ex</span><span class="p">)</span> <span class="o">##</span><span class="n">NEEDED</span><span class="p">.</span>
        <span class="k">ASSERT</span> <span class="mi">1</span> <span class="o">=</span> <span class="mi">2</span><span class="p">.</span>
    <span class="k">ENDTRY</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>

  <span class="k">METHOD</span> <span class="n">constructor</span><span class="p">.</span>
    <span class="n">mv_name</span> <span class="o">=</span> <span class="n">iv_name</span><span class="p">.</span>
    <span class="n">me_enum</span> <span class="o">=</span> <span class="n">ie_enum</span><span class="p">.</span>
    <span class="k">INSERT</span> <span class="k">VALUE</span> <span class="o">#</span><span class="p">(</span> <span class="k">instance</span> <span class="o">=</span> <span class="n">me</span> <span class="n">enum</span> <span class="o">=</span> <span class="n">ie_enum</span> <span class="p">)</span> <span class="k">INTO</span> <span class="k">TABLE</span> <span class="n">gt_registry</span><span class="p">.</span>
    <span class="k">ASSERT</span> <span class="n">sy</span><span class="o">-</span><span class="n">subrc</span> <span class="o">=</span> <span class="mi">0</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>

  <span class="k">METHOD</span> <span class="n">get_next</span><span class="p">.</span>
    <span class="n">ro_next</span> <span class="o">=</span> <span class="k">SWITCH</span> <span class="o">#</span><span class="p">(</span>
      <span class="n">me</span><span class="o">-&gt;</span><span class="n">me_enum</span>
      <span class="k">WHEN</span> <span class="n">gte_light_state</span><span class="o">-</span><span class="k">green</span>          <span class="k">THEN</span> <span class="n">get</span><span class="p">(</span> <span class="n">gte_light_state</span><span class="o">-</span><span class="k">yellow</span> <span class="p">)</span>
      <span class="k">WHEN</span> <span class="n">gte_light_state</span><span class="o">-</span><span class="k">yellow</span>         <span class="k">THEN</span> <span class="n">get</span><span class="p">(</span> <span class="n">gte_light_state</span><span class="o">-</span><span class="k">red</span> <span class="p">)</span>
      <span class="k">WHEN</span> <span class="n">gte_light_state</span><span class="o">-</span><span class="k">red</span>            <span class="k">THEN</span> <span class="n">get</span><span class="p">(</span> <span class="n">gte_light_state</span><span class="o">-</span><span class="n">red_and_yellow</span> <span class="p">)</span>
      <span class="k">WHEN</span> <span class="n">gte_light_state</span><span class="o">-</span><span class="n">red_and_yellow</span> <span class="k">THEN</span> <span class="n">get</span><span class="p">(</span> <span class="n">gte_light_state</span><span class="o">-</span><span class="k">green</span> <span class="p">)</span>
    <span class="p">).</span>
    <span class="k">ASSERT</span> <span class="n">ro_next</span> <span class="k">IS</span> <span class="k">BOUND</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>that is without cheating with the usual suspects (SYSTEM-CALL objmgr CLONE, CALL TRANSFORMATION ID, CL_ABAP_TESTDOUBLE, …) <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>In the documentation’s last chapter under notes you can find information on illegal values being passed to enumeration data objects. According to it other values than the enumeration values and the initial value of the base type can be assigned (allowed by the compiler) to enumeration data objects using dynpros and should therefore never be used with classic dynpros (disallowed by programming guideline). <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Fabian Lupa</name></author><category term="ABAP language" /><category term="ABAP" /><category term="enumerations" /><category term="fixed values" /><category term="type safety" /><summary type="html"><![CDATA[To represent a fixed value set in ABAP you can use several different technologies. The newest one is enumerations which are provided at language level and can be used as of AS ABAP 7.51. With this blog post I want to show which possibilities there are for ABAP developers to define enumerations and use them in signature elements in the safest way possible.]]></summary></entry><entry><title type="html">How To Design Reusable Exception Classes</title><link href="https://lupa.dev/2017/04/03/how-to-design-reusable-exception-classes/" rel="alternate" type="text/html" title="How To Design Reusable Exception Classes" /><published>2017-04-03T00:00:00+00:00</published><updated>2017-04-03T00:00:00+00:00</updated><id>https://lupa.dev/2017/04/03/how-to-design-reusable-exception-classes</id><content type="html" xml:base="https://lupa.dev/2017/04/03/how-to-design-reusable-exception-classes/"><![CDATA[<p>Using the object oriented exception concept I sometimes find myself in a situation that looks like the following.</p>

<p>I define a method that uses an exception class in its signature to indicate some kind of error. So for example this:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CLASS</span> <span class="n">lcl_partner_finder</span> <span class="k">DEFINITION</span> <span class="k">FINAL</span> <span class="k">CREATE</span> <span class="k">PRIVATE</span><span class="p">.</span>
  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">CLASS</span><span class="o">-</span><span class="k">METHODS</span><span class="p">:</span>
      <span class="c1">"! Find a business partner id for a debtor</span>
      <span class="c1">"! @parameter iv_debtor | Debtor id</span>
      <span class="c1">"! @parameter rv_partner | Found business partner id</span>
      <span class="c1">"! @raising lcx_bp_not_found | No business partner exists for iv_debtor</span>
      <span class="n">get_partner_from_debtor</span> <span class="k">IMPORTING</span> <span class="n">iv_debtor</span>         <span class="k">TYPE</span> <span class="n">kunnr</span>
                              <span class="k">RETURNING</span> <span class="nb">VALUE</span><span class="p">(</span><span class="n">rv_partner</span><span class="p">)</span> <span class="k">TYPE</span> <span class="n">bu_partner</span>
                              <span class="k">RAISING</span>   <span class="n">lcx_bp_not_found</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>

<p>According to the <a href="https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abenexception_category_guidl.htm">guidlines for exceptions</a> I have to pick which of the 3 base classes my new exception class LCX_BP_NOT_FOUND uses. In this particular case I think that it would be reasonable to pick CX_STATIC_CHECK because the user of this method cannot validate beforehand if the partner exists, at least not using this class (so no reason for CX_DYNAMIC_CHECK). One could even argue that this method could be used for that exact reason. CX_NO_CHECK is out of the question in my opinion.</p>

<p>So I create the exception class:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">"! Business partner not found exception</span>
<span class="k">CLASS</span> <span class="n">lcx_bp_not_found</span> <span class="k">DEFINITION</span>
  <span class="k">FINAL</span>
  <span class="k">CREATE</span> <span class="k">PUBLIC</span>
  <span class="k">INHERITING</span> <span class="k">FROM</span> <span class="k">cx_static_check</span><span class="p">.</span>

  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">METHODS</span><span class="p">:</span>
      <span class="n">constructor</span> <span class="k">IMPORTING</span> <span class="n">ix_previous</span>       <span class="k">LIKE</span> <span class="n">previous</span> <span class="k">OPTIONAL</span>
                            <span class="n">iv_identification</span> <span class="k">TYPE</span> <span class="kt">csequence</span> <span class="k">OPTIONAL</span><span class="p">,</span>
      <span class="n">get_text</span> <span class="k">REDEFINITION</span><span class="p">.</span>
    <span class="k">DATA</span><span class="p">:</span>
      <span class="n">mv_identification</span> <span class="k">TYPE</span> <span class="k">string</span> <span class="k">READ</span><span class="o">-</span><span class="k">ONLY</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>

<p>Because of the static check any time I call get_partner_from_debtor without a try-catch that includes lcx_bp_not_found or propagation of the exception I will get a syntax warning. Which is what I want, because the probability of it happening is quite high, so the caller should be forced to handle this exception.</p>

<p>Later on in my implementation I define a new method to do something else.</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">"! Check if a business partner has a specified role (currently active)</span>
<span class="c1">"! @parameter iv_partner | Partner id</span>
<span class="c1">"! @parameter iv_role | Role to check</span>
<span class="c1">"! @parameter rv_role_present | Role is present</span>
<span class="n">partner_has_role</span> <span class="k">IMPORTING</span> <span class="n">iv_partner</span>             <span class="k">TYPE</span> <span class="n">bu_partner</span>
                           <span class="n">iv_role</span>                <span class="k">TYPE</span> <span class="n">bu_role</span>
                 <span class="k">RETURNING</span> <span class="nb">VALUE</span><span class="p">(</span><span class="n">rv_role_present</span><span class="p">)</span> <span class="k">TYPE</span> <span class="n">abap_bool</span><span class="p">.</span>
</code></pre></div></div>

<p>Now the first step in implementing it would be to validate the import parameters. So I would check if the partner specified in iv_partner exists. And if it does not I would not continue in the method and instead I would like to raise an exception indicating this error. Above I have already defined an exception class for the case that a business partner could not be found. But in this second method the perspective has changed. I would argue that because iv_partner is a direct importing parameter it is the responsibility of the caller to retrieve the bp id. Most of the users of this method would probably be annoyed if they got a warning to catch a LCX_BP_NOT_FOUND exception because they have implemented code before calling it that guarantees a valid iv_partner actual parameter. According to the guidelines this is the exact use case of CX_DYNAMIC_CHECK exceptions. But my exception is already inheriting from CX_STATIC_CHECK.</p>

<p>My question is, what is the recommended action in this situation? I have the same exception, with the same attributes, the same additional methods, maybe the same T100 message class behind it, but looked at from two different angles.</p>

<p>Approaches I have thought of so far:</p>

<ol>
  <li>Copy the exception class and change the base class, i. e. LCX_BP_NOT_FOUND_DYN_CHECK =&gt; Code duplication, increased maintenance, redundancy, not an option</li>
  <li>Live with many TRY. … CATCH lcx_bp_not_found ##NO_HANDLER. ENDTRY. blocks obfuscating the code base</li>
  <li>Raise a generic dynamic check exception instead, i. e. LCX_ILLEGAL_ARGUMENT and reference the static check one using the PREVIOUS import parameter and pass through the texts with an additional parameter and lx_bp_not_found-&gt;get_text( ) (losing the message class reference and longtext information when displaying the exception via MESSAGE)</li>
</ol>

<p>I most of the time opt for the third approach. One does run into the risk of accidentally propagating the exception though, because the calling method might use LCX_ILLEGAL_ARGUMENT as well on its own. So I am not certain as to what is the ideal solution for this problem.</p>

<p>I am interested in any comments or recommendations.</p>]]></content><author><name>Fabian Lupa</name></author><category term="ABAP language" /><category term="ABAP" /><category term="exceptions" /><category term="exception classes" /><category term="design" /><category term="reusable" /><summary type="html"><![CDATA[Using the object oriented exception concept I sometimes find myself in a situation that looks like the following.]]></summary></entry><entry><title type="html">Avoiding Literals When Using Reflection</title><link href="https://lupa.dev/2017/01/19/avoiding-literals-when-using-reflection/" rel="alternate" type="text/html" title="Avoiding Literals When Using Reflection" /><published>2017-01-19T00:00:00+00:00</published><updated>2017-01-19T00:00:00+00:00</updated><id>https://lupa.dev/2017/01/19/avoiding-literals-when-using-reflection</id><content type="html" xml:base="https://lupa.dev/2017/01/19/avoiding-literals-when-using-reflection/"><![CDATA[<p>ABAP’s reflection (and/or type introspection of you want to make that distinction) capabilities consist mostly of the <code class="language-plaintext highlighter-rouge">CL_ABAP_*DESCR</code>-classes and the <code class="language-plaintext highlighter-rouge">DESCRIBE</code> statement. To get information on a class for example you might have seen code like this:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">DATA</span><span class="p">(</span><span class="n">lo_descr</span><span class="p">)</span> <span class="o">=</span> <span class="k">CAST</span> <span class="n">cl_abap_classdescr</span><span class="p">(</span>
  <span class="n">cl_abap_typedescr</span><span class="o">=&gt;</span><span class="n">describe_by_name</span><span class="p">(</span> <span class="s1">'LCL_TEST'</span> <span class="p">)</span>
<span class="p">).</span>
</code></pre></div></div>

<p>If you have used reflection in other languages you might also be familiar with this code, same principle just a different API and language:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Class</span> <span class="n">clazz</span> <span class="o">=</span> <span class="nc">TestClass</span><span class="o">.</span><span class="na">class</span><span class="o">;</span> <span class="c1">// Java</span>
<span class="nc">Type</span> <span class="n">type</span> <span class="o">=</span> <span class="n">typeof</span><span class="o">(</span><span class="nc">TestClass</span><span class="o">);</span> <span class="c1">// C#</span>
</code></pre></div></div>

<p>One of the differences in comparison to the ABAP code is that you can get a description object of your class without using a literal with the name of the class. This is objectively better because of the following reasons:</p>

<ul>
  <li>Compile time safety: If the class does not exist the code will not compile</li>
  <li>Static usage analysis: Where-Used-List functionality will also find these usages</li>
  <li>Refactoring: If the class name is changed IDE refactoring support will (because of static usage analysis) find these lines of code and offer to change the name there as well. And if you decide against it, because of compile time safety your code will not compile.</li>
</ul>

<p>Using the ABAP example you will unfortunately get runtime errors if the class does not exist or is renamed (unless you catch the exception).
Since ABAP in Eclipse is a thing there are way more refactoring capabilities available than in the ABAP workbench. So I think it is more important than ever to avoid these dynamic runtime usages if possible. So let’s try getting a <code class="language-plaintext highlighter-rouge">CL_ABAP_CLASSDESCR</code> instance where renaming the class using refactoring assistance does not lead to a runtime error.</p>

<h1 id="cl_abap_classdescr-without-classname-literal"><code class="language-plaintext highlighter-rouge">CL_ABAP_CLASSDESCR</code> without classname literal</h1>

<p>To avoid the literal the class name needs to be written in code without quotes and still compile. There are only a few options where this is possible that I can think of:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">INSTANCE OF</code> logical expression in ABAP 7.50</li>
  <li><code class="language-plaintext highlighter-rouge">NEW</code> operator for object instance creation in ABAP 7.40</li>
  <li><code class="language-plaintext highlighter-rouge">DATA</code> variable declaration</li>
  <li><code class="language-plaintext highlighter-rouge">TYPES</code> type definition</li>
  <li><code class="language-plaintext highlighter-rouge">FIELD-SYMBOL</code> declaration</li>
  <li>Static member access using <code class="language-plaintext highlighter-rouge">classname=&gt;member</code></li>
</ol>

<p>Of these 1. does not help because it only results in a logical value, 2. does not help because the class we want to analyze might not be instantiatable (at all or from our position), 6. does not help and 3., 4., and 5. might.</p>

<p>The first approach (which would be just a little bit better) would be to extract the literal to a constant, ideally directly into the class, so that if the class name should change you at least (should) only have one other position to also change it. This would look like this:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CLASS</span> <span class="n">lcl_test</span> <span class="k">DEFINITION</span><span class="p">.</span>
  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">CONSTANTS</span><span class="p">:</span>
      <span class="n">gc_classname</span> <span class="k">TYPE</span> <span class="n">abap_classname</span> <span class="k">VALUE</span> <span class="s1">'LCL_TEST'</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>

<span class="k">DATA</span><span class="p">(</span><span class="n">lo_descr2</span><span class="p">)</span> <span class="o">=</span> <span class="k">CAST</span> <span class="n">cl_abap_classdescr</span><span class="p">(</span>
  <span class="n">cl_abap_typedescr</span><span class="o">=&gt;</span><span class="n">describe_by_name</span><span class="p">(</span> <span class="n">lcl_test</span><span class="o">=&gt;</span><span class="n">gc_classname</span> <span class="p">)</span>
<span class="p">).</span>
</code></pre></div></div>

<p>Good luck redefining that constant in subclasses though…</p>

<p>My personal favorite approach is using option 3:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">DATA</span><span class="p">:</span> <span class="n">lo_dummy</span> <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">lcl_test</span><span class="p">.</span>
<span class="k">DATA</span><span class="p">(</span><span class="n">lo_descr3</span><span class="p">)</span> <span class="o">=</span> <span class="k">CAST</span> <span class="n">cl_abap_classdescr</span><span class="p">(</span>
  <span class="k">CAST</span> <span class="n">cl_abap_refdescr</span><span class="p">(</span>
    <span class="n">cl_abap_typedescr</span><span class="o">=&gt;</span><span class="n">describe_by_data</span><span class="p">(</span> <span class="n">lo_dummy</span> <span class="p">)</span>
  <span class="p">)</span><span class="o">-&gt;</span><span class="n">get_referenced_type</span><span class="p">(</span> <span class="p">)</span>
<span class="p">).</span>
</code></pre></div></div>

<p>With a nullpointer dummy variable of the correct type you can use the <code class="language-plaintext highlighter-rouge">describe_by_data</code> method of the RTTI API and navigate and cast your way towards the class descriptor. It must be pointed out that <code class="language-plaintext highlighter-rouge">describe_by_object_ref</code> does not work because you would need an instance of your class for that (<code class="language-plaintext highlighter-rouge">REFERENCE_IS_INITIAL</code>-exception), so you need to take the detour using <code class="language-plaintext highlighter-rouge">cl_abap_refdescr</code>.</p>

<p>4 and 5 work in a similar way, types is quite interesting I found, because you define a reference type that can the be used by the <code class="language-plaintext highlighter-rouge">VALUE</code> inline statement.</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">TYPES</span><span class="p">:</span> <span class="n">lty_ref</span> <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">lcl_test</span><span class="p">.</span>
<span class="k">DATA</span><span class="p">(</span><span class="n">lo_descr4</span><span class="p">)</span> <span class="o">=</span> <span class="k">CAST</span> <span class="n">cl_abap_classdescr</span><span class="p">(</span>
  <span class="k">CAST</span> <span class="n">cl_abap_refdescr</span><span class="p">(</span>
    <span class="n">cl_abap_typedescr</span><span class="o">=&gt;</span><span class="n">describe_by_data</span><span class="p">(</span> <span class="k">VALUE</span> <span class="n">lty_ref</span><span class="p">(</span> <span class="p">)</span> <span class="p">)</span>
  <span class="p">)</span><span class="o">-&gt;</span><span class="n">get_referenced_type</span><span class="p">(</span> <span class="p">)</span>
<span class="p">).</span>
</code></pre></div></div>

<p>I can not think of a way without using some kind of helper declaration using 7.40 inline magic unfortunately. Here are all of the approaches in action with compatible refactorings highlighted:</p>

<figure class=""><a href="approaches.png" class="image-popup" title="Figure 1: Different approaches in action
"><img src="approaches.png" alt="Screenshot different approaches" /></a><figcaption>
      Figure 1: Different approaches in action

    </figcaption></figure>

<p>These do add quite a bit of complexity to your code so they are ideal candidates for a Z-reflection helper class.</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">DATA</span><span class="p">(</span><span class="n">lo_descr</span><span class="p">)</span> <span class="o">=</span> <span class="n">lcl_reflection_helper</span><span class="o">=&gt;</span><span class="n">get_objectdescr_from_data</span><span class="p">(</span> <span class="n">lo_dummy</span> <span class="p">).</span>
</code></pre></div></div>

<h1 id="getting-identifiers-of-attributes">Getting identifiers of attributes</h1>

<p>Another task you may or may not stumble upon is dynamically getting instance or class members’ values at runtime. The usual approach is this:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CLASS</span> <span class="n">lcl_test</span> <span class="k">DEFINITION</span><span class="p">.</span>
  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">DATA</span><span class="p">:</span>
      <span class="n">mv_member</span> <span class="k">TYPE</span> <span class="k">i</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>

<span class="k">ASSIGN</span> <span class="n">lcl_test</span><span class="o">=&gt;</span><span class="p">(</span><span class="s1">'MV_MEMBER'</span><span class="p">)</span> <span class="k">TO</span> <span class="k">FIELD-SYMBOL</span><span class="p">(</span><span class="o">&lt;</span><span class="n">lg_pointer</span><span class="o">&gt;</span><span class="p">).</span>
<span class="k">ASSERT</span> <span class="o">&lt;</span><span class="n">lg_pointer</span><span class="o">&gt;</span> <span class="k">IS</span> <span class="k">ASSIGNED</span><span class="p">.</span>
<span class="k">WRITE</span> <span class="o">&lt;</span><span class="n">lg_pointer</span><span class="o">&gt;</span><span class="p">.</span>
</code></pre></div></div>

<p>Going a bit further you might think of developing a library that does things on member variables of class instances (like validation or serialization …). One approach could look like this:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">INTERFACE</span> <span class="n">lif_member_publisher</span><span class="p">.</span>
  <span class="k">TYPES</span><span class="p">:</span>
    <span class="n">gty_attribute_name_tab</span> <span class="k">TYPE</span> <span class="k">SORTED</span> <span class="k">TABLE</span> <span class="k">OF</span> <span class="n">abap_attrname</span>
                                <span class="k">WITH</span> <span class="k">UNIQUE</span> <span class="k">KEY</span> <span class="n">table_line</span><span class="p">.</span>
  <span class="k">METHODS</span><span class="p">:</span>
    <span class="n">get_published_members</span>
      <span class="k">RETURNING</span> <span class="nb">VALUE</span><span class="p">(</span><span class="n">rt_members</span><span class="p">)</span> <span class="k">TYPE</span> <span class="n">gty_attribute_name_tab</span><span class="p">.</span>
<span class="k">ENDINTERFACE</span><span class="p">.</span>

<span class="k">CLASS</span> <span class="n">lcl_test</span> <span class="k">DEFINITION</span><span class="p">.</span>
  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">INTERFACES</span><span class="p">:</span>
      <span class="n">lif_member_publisher</span><span class="p">.</span>
    <span class="k">ALIASES</span><span class="p">:</span>
      <span class="n">get_published_members</span> <span class="k">FOR</span> <span class="n">lif_member_publisher</span><span class="o">~</span><span class="n">get_published_members</span><span class="p">.</span>
    <span class="k">DATA</span><span class="p">:</span>
      <span class="n">mv_member</span> <span class="k">TYPE</span> <span class="k">i</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>

<span class="k">CLASS</span> <span class="n">lcl_test</span> <span class="k">IMPLEMENTATION</span><span class="p">.</span>
  <span class="k">METHOD</span> <span class="n">get_published_members</span><span class="p">.</span>
    <span class="n">rt_members</span> <span class="o">=</span> <span class="k">VALUE</span> <span class="o">#</span><span class="p">(</span> <span class="p">(</span> <span class="k">CONV</span> <span class="o">#</span><span class="p">(</span> <span class="s1">'MV_MEMBER'</span> <span class="p">)</span> <span class="p">)</span> <span class="p">).</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>


<span class="k">DATA</span><span class="p">(</span><span class="n">lo_test</span><span class="p">)</span> <span class="o">=</span> <span class="k">NEW</span> <span class="n">lcl_test</span><span class="p">(</span> <span class="p">).</span>
<span class="k">LOOP</span> <span class="k">AT</span> <span class="n">lo_test</span><span class="o">-&gt;</span><span class="n">get_published_members</span><span class="p">(</span> <span class="p">)</span>
     <span class="k">ASSIGNING</span> <span class="k">FIELD-SYMBOL</span><span class="p">(</span><span class="o">&lt;</span><span class="n">lv_member_name</span><span class="o">&gt;</span><span class="p">).</span>
  <span class="k">ASSIGN</span> <span class="n">lo_test</span><span class="o">-&gt;</span><span class="p">(</span><span class="o">&lt;</span><span class="n">lv_member_name</span><span class="o">&gt;</span><span class="p">)</span> <span class="k">TO</span> <span class="k">FIELD-SYMBOL</span><span class="p">(</span><span class="o">&lt;</span><span class="n">lg_pointer</span><span class="o">&gt;</span><span class="p">).</span>
  <span class="k">ASSERT</span> <span class="o">&lt;</span><span class="n">lg_pointer</span><span class="o">&gt;</span> <span class="k">IS</span> <span class="k">ASSIGNED</span><span class="p">.</span>
  <span class="k">WRITE</span><span class="p">:</span> <span class="o">/</span> <span class="p">|{</span> <span class="o">&lt;</span><span class="n">lv_member_name</span><span class="o">&gt;</span> <span class="p">}:</span> <span class="p">{</span> <span class="o">&lt;</span><span class="n">lg_pointer</span><span class="o">&gt;</span> <span class="p">}|.</span>
<span class="k">ENDLOOP</span><span class="p">.</span>
</code></pre></div></div>

<p>While this already works quite well we again have a literal for an identifier in our code. This will lead to an assertion error at <code class="language-plaintext highlighter-rouge">ASSERT &lt;lg_pointer&gt; IS ASSIGNED</code> if the member identifier is changed while the literal is not and again refactoring utilities cannot statically find this usage.</p>

<p>In C# there is a statement at language level to avoid this exact problem called <code class="language-plaintext highlighter-rouge">nameof</code> (<a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/nameof?redirectedfrom=MSDN">documentation</a>). C# especially needs it because of data bindings in XAML / using the MVVM pattern.</p>

<p>In ABAP there is no such statement that I am aware of. But there is a loophole you can use:<br />
When comparing data reference variables (<code class="language-plaintext highlighter-rouge">TYPE REF TO data</code>) that are type compatible the logical expression will be true if they point to the same data object (<a href="https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abenlogexp_rules_operands_ref.htm">source</a>). So one can just compare all member variables of a class with a given one and where the expression is true that is the searched member.
Translated to the example above:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CLASS</span> <span class="n">lcl_test</span> <span class="k">IMPLEMENTATION</span><span class="p">.</span>
  <span class="k">METHOD</span> <span class="n">get_published_members</span><span class="p">.</span>
    <span class="k">DATA</span><span class="p">(</span><span class="n">lr_ref</span><span class="p">)</span> <span class="o">=</span> <span class="k">REF</span> <span class="o">#</span><span class="p">(</span> <span class="n">mv_member</span> <span class="p">).</span>
    <span class="k">DATA</span><span class="p">(</span><span class="n">lo_descr</span><span class="p">)</span> <span class="o">=</span> <span class="k">CAST</span> <span class="n">cl_abap_classdescr</span><span class="p">(</span>
      <span class="n">cl_abap_typedescr</span><span class="o">=&gt;</span><span class="n">describe_by_object_ref</span><span class="p">(</span> <span class="n">me</span> <span class="p">)</span>
    <span class="p">).</span>

    <span class="k">LOOP</span> <span class="k">AT</span> <span class="n">lo_descr</span><span class="o">-&gt;</span><span class="k">attributes</span> <span class="k">ASSIGNING</span> <span class="k">FIELD-SYMBOL</span><span class="p">(</span><span class="o">&lt;</span><span class="n">ls_attr</span><span class="o">&gt;</span><span class="p">).</span>
      <span class="k">ASSIGN</span> <span class="n">me</span><span class="o">-&gt;</span><span class="p">(</span><span class="o">&lt;</span><span class="n">ls_attr</span><span class="o">&gt;-</span><span class="k">name</span><span class="p">)</span> <span class="k">TO</span> <span class="k">FIELD-SYMBOL</span><span class="p">(</span><span class="o">&lt;</span><span class="n">lg_pointer</span><span class="o">&gt;</span><span class="p">).</span>
      <span class="k">ASSERT</span> <span class="o">&lt;</span><span class="n">lg_pointer</span><span class="o">&gt;</span> <span class="k">IS</span> <span class="k">ASSIGNED</span><span class="p">.</span>
      <span class="k">IF</span> <span class="n">lr_ref</span> <span class="o">=</span> <span class="k">REF</span> <span class="o">#</span><span class="p">(</span> <span class="o">&lt;</span><span class="n">lg_pointer</span><span class="o">&gt;</span> <span class="p">).</span>
        <span class="n">rt_members</span> <span class="o">=</span> <span class="k">VALUE</span> <span class="o">#</span><span class="p">(</span> <span class="p">(</span> <span class="o">&lt;</span><span class="n">ls_attr</span><span class="o">&gt;-</span><span class="k">name</span> <span class="p">)</span> <span class="p">).</span>
        <span class="k">EXIT</span><span class="p">.</span>
      <span class="k">ENDIF</span><span class="p">.</span>
      <span class="k">UNASSIGN</span> <span class="o">&lt;</span><span class="n">lg_pointer</span><span class="o">&gt;</span><span class="p">.</span>
    <span class="k">ENDLOOP</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>

<p>And if you extract that functionality to a helper method it does not even look all too bad:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CLASS</span> <span class="n">lcl_test</span> <span class="k">IMPLEMENTATION</span><span class="p">.</span>
  <span class="k">METHOD</span> <span class="n">get_published_members</span><span class="p">.</span>
    <span class="n">rt_members</span> <span class="o">=</span> <span class="k">VALUE</span> <span class="o">#</span><span class="p">(</span>
      <span class="p">(</span> <span class="n">lcl_reflection_helper</span><span class="o">=&gt;</span><span class="n">name_of</span><span class="p">(</span>
          <span class="n">io_container</span> <span class="o">=</span> <span class="n">me</span> <span class="n">ir_ref</span> <span class="o">=</span> <span class="k">REF</span> <span class="o">#</span><span class="p">(</span> <span class="n">mv_member</span> <span class="p">)</span>
        <span class="p">)</span> <span class="p">)</span>
    <span class="p">).</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>

<p>You do however lose quite a bit of performance if there are a lot of member variables to go through, so if you decide on using a strategy like this you might also want to cache the results for reuse.</p>

<h1 id="helper-class">Helper class</h1>

<p>And here’s a little helper class for both cases discussed. It might not factor in all edge cases so adjust as needed if you want to use it.</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">"! Reflection tools</span>
<span class="k">CLASS</span> <span class="n">lcl_reflection_helper</span> <span class="k">DEFINITION</span><span class="p">.</span>
  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">CLASS</span><span class="o">-</span><span class="k">METHODS</span><span class="p">:</span>
      <span class="c1">"! Get objectdescriptor from data variable</span>
      <span class="c1">"! @parameter ig_data | Variable typed as REF TO interface/class</span>
      <span class="c1">"! @parameter ro_descriptor | Object descriptor</span>
      <span class="c1">"! @raising lcx_illegal_argument | ig_data does not refer to a class / interface</span>
      <span class="n">get_objectdescr_from_data</span>
        <span class="k">IMPORTING</span> <span class="n">ig_data</span>              <span class="k">TYPE</span> <span class="k">any</span>
        <span class="k">RETURNING</span> <span class="nb">VALUE</span><span class="p">(</span><span class="n">ro_descriptor</span><span class="p">)</span> <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="n">cl_abap_objectdescr</span>
        <span class="k">RAISING</span>   <span class="n">lcx_illegal_argument</span><span class="p">,</span>
      <span class="c1">"! Get the identifier of a public variable</span>
      <span class="c1">"! @parameter ir_ref | Reference to the variable</span>
      <span class="c1">"! @parameter io_container | Containing object instance</span>
      <span class="c1">"! @parameter rv_name | Identifier of the data object</span>
      <span class="c1">"! @raising lcx_illegal_argument | Argument cannot be null</span>
      <span class="n">name_of</span> <span class="k">IMPORTING</span> <span class="n">ir_ref</span>         <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="k">data</span>
                        <span class="n">io_container</span>   <span class="k">TYPE</span> <span class="k">REF</span> <span class="k">TO</span> <span class="k">object</span>
              <span class="k">RETURNING</span> <span class="nb">VALUE</span><span class="p">(</span><span class="n">rv_name</span><span class="p">)</span> <span class="k">TYPE</span> <span class="n">abap_attrname</span>
              <span class="k">RAISING</span> <span class="n">lcx_illegal_argument</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>

<span class="k">CLASS</span> <span class="n">lcl_reflection_helper</span> <span class="k">IMPLEMENTATION</span><span class="p">.</span>
  <span class="k">METHOD</span> <span class="n">get_objectdescr_from_data</span><span class="p">.</span>
    <span class="k">DATA</span><span class="p">(</span><span class="n">lo_descr</span><span class="p">)</span> <span class="o">=</span> <span class="n">cl_abap_typedescr</span><span class="o">=&gt;</span><span class="n">describe_by_data</span><span class="p">(</span> <span class="n">ig_data</span> <span class="p">).</span>

    <span class="k">IF</span> <span class="n">lo_descr</span><span class="o">-&gt;</span><span class="n">type_kind</span> <span class="o">&lt;&gt;</span> <span class="n">cl_abap_typedescr</span><span class="o">=&gt;</span><span class="n">typekind_oref</span><span class="p">.</span>
      <span class="k">RAISE</span> <span class="k">EXCEPTION</span> <span class="k">TYPE</span> <span class="n">lcx_illegal_argument</span><span class="p">.</span>
    <span class="k">ENDIF</span><span class="p">.</span>

    <span class="k">DATA</span><span class="p">(</span><span class="n">lo_referenced_descr</span><span class="p">)</span> <span class="o">=</span> <span class="k">CAST</span> <span class="n">cl_abap_refdescr</span><span class="p">(</span> <span class="n">lo_descr</span>
      <span class="p">)</span><span class="o">-&gt;</span><span class="n">get_referenced_type</span><span class="p">(</span> <span class="p">).</span>

    <span class="k">IF</span> <span class="n">lo_referenced_descr</span><span class="o">-&gt;</span><span class="n">type_kind</span> <span class="o">&lt;&gt;</span> <span class="n">cl_abap_typedescr</span><span class="o">=&gt;</span><span class="n">typekind_class</span>
        <span class="k">AND</span> <span class="n">lo_referenced_descr</span><span class="o">-&gt;</span><span class="n">type_kind</span> <span class="o">&lt;&gt;</span> <span class="n">cl_abap_typedescr</span><span class="o">=&gt;</span><span class="n">typekind_intf</span><span class="p">.</span>
      <span class="k">RAISE</span> <span class="k">EXCEPTION</span> <span class="k">TYPE</span> <span class="n">lcx_illegal_argument</span><span class="p">.</span>
    <span class="k">ENDIF</span><span class="p">.</span>

    <span class="n">ro_descriptor</span> <span class="o">=</span> <span class="k">CAST</span> <span class="n">cl_abap_objectdescr</span><span class="p">(</span> <span class="n">lo_referenced_descr</span> <span class="p">).</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>

  <span class="k">METHOD</span> <span class="n">name_of</span><span class="p">.</span>
    <span class="k">IF</span> <span class="n">io_container</span> <span class="k">IS</span> <span class="k">NOT</span> <span class="k">BOUND</span> <span class="k">OR</span> <span class="n">ir_ref</span> <span class="k">IS</span> <span class="k">NOT</span> <span class="k">BOUND</span><span class="p">.</span>
      <span class="k">RAISE</span> <span class="k">EXCEPTION</span> <span class="k">TYPE</span> <span class="n">lcx_illegal_argument</span><span class="p">.</span>
    <span class="k">ENDIF</span><span class="p">.</span>

    <span class="k">DATA</span><span class="p">(</span><span class="n">lo_descr</span><span class="p">)</span> <span class="o">=</span> <span class="k">CAST</span> <span class="n">cl_abap_objectdescr</span><span class="p">(</span>
                       <span class="n">cl_abap_typedescr</span><span class="o">=&gt;</span><span class="n">describe_by_object_ref</span><span class="p">(</span> <span class="n">io_container</span> <span class="p">)</span>
                     <span class="p">).</span>

    <span class="k">LOOP</span> <span class="k">AT</span> <span class="n">lo_descr</span><span class="o">-&gt;</span><span class="k">attributes</span> <span class="k">ASSIGNING</span> <span class="k">FIELD-SYMBOL</span><span class="p">(</span><span class="o">&lt;</span><span class="n">ls_attr</span><span class="o">&gt;</span><span class="p">)</span>
         <span class="k">WHERE</span> <span class="n">visibility</span> <span class="o">=</span> <span class="n">cl_abap_objectdescr</span><span class="o">=&gt;</span><span class="k">public</span><span class="p">.</span>

      <span class="k">ASSIGN</span> <span class="n">io_container</span><span class="o">-&gt;</span><span class="p">(</span><span class="o">&lt;</span><span class="n">ls_attr</span><span class="o">&gt;-</span><span class="k">name</span><span class="p">)</span> <span class="k">TO</span> <span class="k">FIELD-SYMBOL</span><span class="p">(</span><span class="o">&lt;</span><span class="n">lg_pointer</span><span class="o">&gt;</span><span class="p">).</span>
      <span class="k">ASSERT</span> <span class="o">&lt;</span><span class="n">lg_pointer</span><span class="o">&gt;</span> <span class="k">IS</span> <span class="k">ASSIGNED</span><span class="p">.</span>
      <span class="k">IF</span> <span class="n">ir_ref</span> <span class="o">=</span> <span class="k">REF</span> <span class="o">#</span><span class="p">(</span> <span class="o">&lt;</span><span class="n">lg_pointer</span><span class="o">&gt;</span> <span class="p">).</span>
        <span class="n">rv_name</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">ls_attr</span><span class="o">&gt;-</span><span class="k">name</span><span class="p">.</span>
        <span class="k">EXIT</span><span class="p">.</span>
      <span class="k">ENDIF</span><span class="p">.</span>
      <span class="k">UNASSIGN</span> <span class="o">&lt;</span><span class="n">lg_pointer</span><span class="o">&gt;</span><span class="p">.</span>
    <span class="k">ENDLOOP</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>]]></content><author><name>Fabian Lupa</name></author><category term="ABAP language" /><category term="ABAP" /><category term="reflection" /><category term="RTTI" /><category term="type safety" /><summary type="html"><![CDATA[ABAP’s reflection (and/or type introspection of you want to make that distinction) capabilities consist mostly of the CL_ABAP_*DESCR-classes and the DESCRIBE statement. To get information on a class for example you might have seen code like this:]]></summary></entry><entry><title type="html">Additional ADT Code Templates</title><link href="https://lupa.dev/2017/01/16/additional-adt-code-templates/" rel="alternate" type="text/html" title="Additional ADT Code Templates" /><published>2017-01-16T00:00:00+00:00</published><updated>2017-01-16T00:00:00+00:00</updated><id>https://lupa.dev/2017/01/16/additional-adt-code-templates</id><content type="html" xml:base="https://lupa.dev/2017/01/16/additional-adt-code-templates/"><![CDATA[<p>In the ABAP Development Tools for Eclipse one useful feature is code templates. With them you can easily insert boilerplate template code and adjust it as necessary. ADT already comes with many available, but you can also define your own, some of which I want to share as others might find them useful too.</p>

<figure class=""><a href="code-templates.png" class="image-popup" title="Figure 1: Available Code Templates in ADT
"><img src="code-templates.png" alt="Screenshot of available code templates in ADT" /></a><figcaption>
      Figure 1: Available Code Templates in ADT

    </figcaption></figure>

<h1 id="templates">Templates</h1>
<h2 id="exception-class">Exception class</h2>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">"! ${descr}</span>
<span class="k">CLASS</span> <span class="err">$</span><span class="p">{</span><span class="n">enclosing_object</span><span class="p">}</span> <span class="k">DEFINITION</span>
  <span class="k">PUBLIC</span>
  <span class="k">INHERITING</span> <span class="k">FROM</span> <span class="err">$</span><span class="p">{</span><span class="k">base</span><span class="p">}</span>
  <span class="k">FINAL</span>
  <span class="k">CREATE</span> <span class="k">PUBLIC</span><span class="p">.</span>

  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">CONSTANTS</span><span class="p">:</span>
      <span class="k">BEGIN</span> <span class="k">OF</span> <span class="n">gc_no_arguments</span><span class="p">,</span>
        <span class="n">msgid</span> <span class="k">TYPE</span> <span class="n">symsgid</span> <span class="k">VALUE</span> <span class="s1">'${msgid}'</span><span class="p">,</span>
        <span class="n">msgno</span> <span class="k">TYPE</span> <span class="n">symsgno</span> <span class="k">VALUE</span> <span class="s1">'${msgno}'</span><span class="p">,</span>
        <span class="n">attr1</span> <span class="k">TYPE</span> <span class="n">scx_attrname</span> <span class="k">VALUE</span> <span class="s1">''</span><span class="p">,</span>
        <span class="n">attr2</span> <span class="k">TYPE</span> <span class="n">scx_attrname</span> <span class="k">VALUE</span> <span class="s1">''</span><span class="p">,</span>
        <span class="n">attr3</span> <span class="k">TYPE</span> <span class="n">scx_attrname</span> <span class="k">VALUE</span> <span class="s1">''</span><span class="p">,</span>
        <span class="n">attr4</span> <span class="k">TYPE</span> <span class="n">scx_attrname</span> <span class="k">VALUE</span> <span class="s1">''</span><span class="p">,</span>
      <span class="k">END</span> <span class="k">OF</span> <span class="n">gc_no_arguments</span><span class="p">.</span>
    <span class="k">INTERFACES</span><span class="p">:</span>
      <span class="k">if_t100_message</span><span class="p">.</span>
    <span class="k">METHODS</span><span class="p">:</span>
      <span class="c1">"! @parameter is_textid | Textid</span>
      <span class="c1">"! @parameter ix_previous | Previous exception</span>
      <span class="n">constructor</span> <span class="k">IMPORTING</span> <span class="n">is_textid</span>    <span class="k">LIKE</span> <span class="k">if_t100_message</span><span class="o">=&gt;</span><span class="n">t100key</span> <span class="k">OPTIONAL</span>
                            <span class="n">ix_previous</span>  <span class="k">LIKE</span> <span class="n">previous</span> <span class="k">OPTIONAL</span><span class="p">.</span>
  <span class="k">PROTECTED</span> <span class="k">SECTION</span><span class="p">.</span>
  <span class="k">PRIVATE</span> <span class="k">SECTION</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>



<span class="k">CLASS</span> <span class="err">$</span><span class="p">{</span><span class="n">enclosing_object</span><span class="p">}</span> <span class="k">IMPLEMENTATION</span><span class="p">.</span>
  <span class="k">METHOD</span> <span class="n">constructor</span><span class="p">.</span>
    <span class="n">super</span><span class="o">-&gt;</span><span class="n">constructor</span><span class="p">(</span> <span class="n">previous</span> <span class="o">=</span> <span class="n">ix_previous</span> <span class="p">).</span>

    <span class="k">CLEAR</span> <span class="n">me</span><span class="o">-&gt;</span><span class="n">textid</span><span class="p">.</span>
    <span class="k">IF</span> <span class="n">is_textid</span> <span class="k">IS</span> <span class="k">INITIAL</span><span class="p">.</span>
      <span class="k">if_t100_message</span><span class="o">~</span><span class="n">t100key</span> <span class="o">=</span> <span class="n">gc_no_arguments</span><span class="p">.</span>
    <span class="k">ELSE</span><span class="p">.</span>
      <span class="k">if_t100_message</span><span class="o">~</span><span class="n">t100key</span> <span class="o">=</span> <span class="n">is_textid</span><span class="p">.</span>
    <span class="k">ENDIF</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>

<h2 id="include-file-header">Include file header</h2>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">*----------------------------------------------------------------------*</span>
<span class="cm">***INCLUDE ${enclosing_object}.</span>
<span class="cm">*----------------------------------------------------------------------*</span>
</code></pre></div></div>

<h2 id="message-dummy">Message dummy</h2>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">MESSAGE</span> <span class="err">$</span><span class="p">{</span><span class="n">msgtyno</span><span class="p">}(</span><span class="err">$</span><span class="p">{</span><span class="n">msgid</span><span class="p">})</span> <span class="k">WITH</span> <span class="err">$</span><span class="p">{</span><span class="n">msgv</span><span class="p">}</span> <span class="k">INTO</span> <span class="k">DATA</span><span class="p">(</span><span class="err">$</span><span class="p">{</span><span class="n">lv_dummy</span><span class="p">})</span> <span class="o">##</span><span class="n">NEEDED</span><span class="p">.</span>
</code></pre></div></div>

<h2 id="message-to-text">Message to text</h2>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">MESSAGE</span> <span class="k">ID</span> <span class="n">sy</span><span class="o">-</span><span class="n">msgid</span> <span class="k">TYPE</span> <span class="n">sy</span><span class="o">-</span><span class="n">msgty</span> <span class="k">NUMBER</span> <span class="n">sy</span><span class="o">-</span><span class="n">msgno</span>
        <span class="k">WITH</span> <span class="n">sy</span><span class="o">-</span><span class="n">msgv1</span> <span class="n">sy</span><span class="o">-</span><span class="n">msgv2</span> <span class="n">sy</span><span class="o">-</span><span class="n">msgv3</span> <span class="n">sy</span><span class="o">-</span><span class="n">msgv4</span>
        <span class="k">INTO</span> <span class="k">DATA</span><span class="p">(</span><span class="err">$</span><span class="p">{</span><span class="n">lv_msg_text</span><span class="p">}).</span>
</code></pre></div></div>

<h2 id="initialize-object-services">Initialize object services</h2>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cl_os_system</span><span class="o">=&gt;</span><span class="n">init_and_set_modes</span><span class="p">(</span> <span class="n">i_external_commit</span> <span class="o">=</span> <span class="err">$</span><span class="p">{</span><span class="n">oscon_false</span><span class="p">}</span>
                                  <span class="n">i_update_mode</span>     <span class="o">=</span> <span class="err">$</span><span class="p">{</span><span class="n">oscon_dmode_update_task</span><span class="p">}</span> <span class="p">).</span>
</code></pre></div></div>

<h2 id="report-header">Report header</h2>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">*&amp;--------------------------------------------------------------------*</span>
<span class="cm">*&amp; Program               ${enclosing_object}</span>
<span class="cm">*&amp; Author                ${author}</span>
<span class="cm">*&amp; Short description     ${shortText}</span>
<span class="cm">*&amp;--------------------------------------------------------------------*</span>
<span class="cm">*&amp; Date                  ${year}-${month}-${day}</span>
<span class="cm">*&amp; Last change           ${year}-${month}-${day}</span>
<span class="cm">*&amp;--------------------------------------------------------------------*</span>
<span class="cm">*&amp; Change protocol</span>
<span class="cm">*&amp; Date       User         Description</span>
<span class="cm">*&amp; ____-__-__ ____________ ______________________________________</span>
<span class="cm">*&amp;--------------------------------------------------------------------*</span>
</code></pre></div></div>

<h1 id="installation">Installation</h1>

<p>If you want to install all of them you can Right Click -&gt; Save as… on the following link and import the xml file in the code template settings (Preferences -&gt; ABAP Development -&gt; Editors -&gt; Source Code Editors -&gt; ABAP Templates -&gt; Import…).</p>

<p><a href="https://gist.githubusercontent.com/fabianlupa/6c6c01cc2386bc65f7217061974c7c5b/raw/5b88c5904997b95f2349a7773a8a40e43e607558/adt-templates.xml">Download link</a></p>

<p>Further reading:</p>

<ul>
  <li><a href="https://blogs.sap.com/2013/02/01/insert-code-patterns-and-templates-within-eclipse/">Insert Code Patterns and Templates within Eclipse</a></li>
  <li><a href="https://blogs.sap.com/2013/05/24/creating-new-code-templates-with-abap-in-eclipse/">Creating new Code Templates with ABAP in Eclipse</a></li>
</ul>]]></content><author><name>Fabian Lupa</name></author><category term="developer tools" /><category term="ADT" /><category term="ABAP Development Tools" /><category term="code templates" /><summary type="html"><![CDATA[In the ABAP Development Tools for Eclipse one useful feature is code templates. With them you can easily insert boilerplate template code and adjust it as necessary. ADT already comes with many available, but you can also define your own, some of which I want to share as others might find them useful too.]]></summary></entry><entry><title type="html">Using Chained Statements To Imitate Varargs in ABAP</title><link href="https://lupa.dev/2016/12/30/using-chained-statements-to-imitiate-varargs-in-abap/" rel="alternate" type="text/html" title="Using Chained Statements To Imitate Varargs in ABAP" /><published>2016-12-30T00:00:00+00:00</published><updated>2016-12-30T00:00:00+00:00</updated><id>https://lupa.dev/2016/12/30/using-chained-statements-to-imitiate-varargs-in-abap</id><content type="html" xml:base="https://lupa.dev/2016/12/30/using-chained-statements-to-imitiate-varargs-in-abap/"><![CDATA[<p class="notice--warning"><strong>Note:</strong> This post dates back to 2016. Although using chained statements in this way is still technically possible, most programming guidelines advise against their use outside of declarations. With the introduction of new expressions in ABAP 7.40, the need for chained statements and macros has become very rare.</p>

<p>In other programming languages (like Java for example) there is a concept called varargs for method parameters. Instead of having to build an array by hand you can just pass your elements separated by a comma to the method and the runtime/compiler will make an array for you and pass it to the method. It looks like this:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">log</span><span class="o">(</span><span class="nc">String</span><span class="o">...</span> <span class="n">entries</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="n">entries</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">::</span><span class="n">println</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Using an array as the actual parameter</span>
<span class="nc">String</span><span class="o">[]</span> <span class="n">entries</span> <span class="o">=</span> <span class="o">{</span><span class="s">"Log entry one"</span><span class="o">,</span> <span class="s">"Log entry two"</span><span class="o">,</span> <span class="s">"Log entry three"</span><span class="o">};</span>
<span class="n">log</span><span class="o">(</span><span class="n">entries</span><span class="o">);</span>

<span class="c1">// Using varargs</span>
<span class="n">log</span><span class="o">(</span><span class="s">"Log entry one"</span><span class="o">,</span> <span class="s">"Log entry two"</span><span class="o">,</span> <span class="s">"Log entry three"</span><span class="o">);</span>
</code></pre></div></div>

<p>In ABAP however there is no concept of varargs. It actually is in conflict with named parameter passing and optional and default parameters. So for the logging method above we would probably write two methods instead, one taking only a single log entry as an argument and the other taking a table of log entries so that the caller does not need to wrap the method call in a loop, like this:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CLASS</span> <span class="n">lcl_logger</span> <span class="k">DEFINITION</span><span class="p">.</span>
  <span class="k">PUBLIC</span> <span class="k">SECTION</span><span class="p">.</span>
    <span class="k">CLASS</span><span class="o">-</span><span class="k">METHODS</span><span class="p">:</span>
      <span class="n">log</span> <span class="k">IMPORTING</span> <span class="n">iv_text</span> <span class="k">TYPE</span> <span class="k">string</span><span class="p">,</span>
      <span class="n">log_table</span> <span class="k">IMPORTING</span> <span class="n">it_entries</span> <span class="k">TYPE</span> <span class="n">stringtab</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>

<span class="k">CLASS</span> <span class="n">lcl_logger</span> <span class="k">IMPLEMENTATION</span><span class="p">.</span>
  <span class="k">METHOD</span> <span class="n">log</span><span class="p">.</span>
    <span class="k">WRITE</span><span class="p">:</span> <span class="o">/</span> <span class="n">iv_text</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>

  <span class="k">METHOD</span> <span class="n">log_table</span><span class="p">.</span>
    <span class="k">LOOP</span> <span class="k">AT</span> <span class="n">it_entries</span> <span class="k">ASSIGNING</span> <span class="k">FIELD-SYMBOL</span><span class="p">(</span><span class="o">&lt;</span><span class="n">lv_text</span><span class="o">&gt;</span><span class="p">).</span>
      <span class="nb">log</span><span class="p">(</span> <span class="o">&lt;</span><span class="n">lv_text</span><span class="o">&gt;</span> <span class="p">).</span>
    <span class="k">ENDLOOP</span><span class="p">.</span>
  <span class="k">ENDMETHOD</span><span class="p">.</span>
<span class="k">ENDCLASS</span><span class="p">.</span>
</code></pre></div></div>

<p>With 7.40 ABAP syntax the <code class="language-plaintext highlighter-rouge">log_table</code>-call is not too bad, as you can just initialize the internal table inline using the VALUE statement, like this:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">log_table</span><span class="p">(</span> <span class="k">VALUE</span> <span class="o">#</span><span class="p">(</span> <span class="p">(</span> <span class="s1">`Log entry one`</span> <span class="p">)</span> <span class="p">(</span> <span class="s1">`Log entry two`</span> <span class="p">)</span> <span class="p">(</span> <span class="s1">`Log entry three`</span> <span class="p">)</span> <span class="p">)</span> <span class="p">).</span>
</code></pre></div></div>

<p>But there is also another possibility which does not need the <code class="language-plaintext highlighter-rouge">log_table</code>-method at all. I have never seen it in any ABAP code until I recently randomly saw a code section in an ABAP book where a chained statement was used in a functional method call, which I did not even know was possible. With that you could do something like this:</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">log</span><span class="p">(:</span>
  <span class="s1">`Log entry one`</span> <span class="p">),</span>
  <span class="s1">`Log entry two`</span> <span class="p">),</span>
  <span class="s1">`Log entry three`</span>
<span class="p">).</span>
</code></pre></div></div>

<p>This is not technically related to varargs because at compilation the chained statement will just be split up in individual method calls and the method will be called three times. However, the syntax for the call looks similar and there might be some situations where it could be easier to read or maintain than individual method calls or wrapping them in a helper method.
A word of warning though. In the <a href="http://help.sap.com/abapdocu_751/en/abenchained_statements_guidl.htm">programming guidelines for chained statements</a> there is a chapter on method calls where it warns you about using them with more advanced interfaces or techniques. For example returning parameters and expressions using them especially with inline declarations in interaction with chained statements will lead to unexpected behavior.</p>

<p>Here’s a more advanced example using <code class="language-plaintext highlighter-rouge">CL_SALV_TREE</code>-methods. One can argue if it increases or decreases readability and maintainability.</p>

<div class="language-abap highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">mo_object_tree</span><span class="o">-&gt;</span><span class="n">get_columns</span><span class="p">(</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">get_column</span><span class="p">(:</span>
  <span class="s1">'OBJECTNAME'</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">set_visible</span><span class="p">(</span> <span class="n">abap_false</span> <span class="p">),</span>
  <span class="s1">'COMPONENT'</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">set_visible</span><span class="p">(</span> <span class="n">abap_false</span> <span class="p">),</span>
  <span class="s1">'COMPONENTTYPE'</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">set_visible</span><span class="p">(</span> <span class="n">abap_false</span> <span class="p">),</span>
  <span class="s1">'PARENTCOMPONENT'</span> <span class="p">)</span><span class="o">-&gt;</span><span class="n">set_visible</span><span class="p">(</span> <span class="n">abap_false</span> <span class="p">)</span>
<span class="p">.</span>
</code></pre></div></div>]]></content><author><name>Fabian Lupa</name></author><category term="ABAP language" /><category term="ABAP" /><category term="varargs" /><category term="chained statements" /><summary type="html"><![CDATA[In other programming languages (like Java for example) there is a concept called varargs for method parameters. Instead of having to build an array by hand you can just pass your elements separated by a comma to the method and the runtime/compiler will make an array for you and pass it to the method. It looks like this...]]></summary></entry></feed>