<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Kosmik</title>
    <description>The latest articles on Forem by Kosmik (@jtama).</description>
    <link>https://forem.com/jtama</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F233182%2F93d11d17-5449-4f73-87d6-a86e333008f4.gif</url>
      <title>Forem: Kosmik</title>
      <link>https://forem.com/jtama</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jtama"/>
    <language>en</language>
    <item>
      <title>Generating an aerial view of your project with OpenRewrite</title>
      <dc:creator>Kosmik</dc:creator>
      <pubDate>Wed, 25 Mar 2026 13:10:52 +0000</pubDate>
      <link>https://forem.com/onepoint/generating-an-aerial-view-of-your-project-with-openrewrite-49kk</link>
      <guid>https://forem.com/onepoint/generating-an-aerial-view-of-your-project-with-openrewrite-49kk</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Read it in &lt;a href="https://jtama.github.io/posts/g-n-rer-une-vue-a-rienne-de-votre-projet-avec-openrewrite/" rel="noopener noreferrer"&gt;french here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the previous article, we discussed (well, &lt;em&gt;I wrote, you read&lt;/em&gt;) &lt;a href="https://jtama.github.io/posts/technique-avanc-e-d-openrewrite-orchestrer-des-refactorings-complexes-avec-les-scanningrecipe/" rel="noopener noreferrer"&gt;Scanning Recipes&lt;/a&gt; when you need complete information to make a decision, or to generate code &lt;em&gt;ex nihilo&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I promised you a little toy at the end... The time has come for the reveal: the &lt;strong&gt;Project Graph Generator&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5u8t7btttr934mtnn9l2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5u8t7btttr934mtnn9l2.gif" alt="Aerial view of the architecture generated by the tool" width="200" height="94"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Need: Mapping Your Code
&lt;/h2&gt;

&lt;p&gt;It’s sometimes difficult to get a global view of the dependencies within your own code. Is this package too tightly coupled to others? What is the central class of my domain? Has my wonderful initial design withstood the ravages of time?&lt;/p&gt;

&lt;p&gt;Since &lt;a href="https://docs.openrewrite.org/" rel="noopener noreferrer"&gt;OpenRewrite&lt;/a&gt; already knows how to build the complete &lt;strong&gt;LST&lt;/strong&gt; of your codebase, it’s not &lt;em&gt;exactly&lt;/em&gt; complicated to traverse it to deduce relationships.&lt;/p&gt;

&lt;p&gt;This is exactly the goal of the &lt;a href="https://github.com/jtama/project-graph-generator" rel="noopener noreferrer"&gt;project-graph-generator&lt;/a&gt; project: scanning your sources to deduce a dependency graph and produce a simple HTML page using &lt;a href="https://d3js.org/" rel="noopener noreferrer"&gt;D3.js&lt;/a&gt; to display it.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Oh thou, who doesn’t want to know more, but only wants to play with the graph, go no further than this chapter, you might gain some knowledge!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The project is easily usable via the &lt;code&gt;rewrite-maven-plugin&lt;/code&gt; &lt;a href="https://docs.openrewrite.org/running-recipes" rel="noopener noreferrer"&gt;or any other way to trigger an OpenRewrite recipe&lt;/a&gt;. Here is the command to launch a complete analysis of your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
-Drewrite.recipeArtifactCoordinates=io.github.jtama:project-graph-generator:RELEASE \ ①
-Drewrite.activeRecipes=io.github.jtama.openrewrite.ProjectAerialViewGenerator \
-Drewrite.exportDatatables=true
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;RELEASE&lt;/code&gt; = &lt;code&gt;latest&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once the analysis is complete, the plugin generates a standalone HTML file containing the data AND the visualization. Simply open the &lt;code&gt;class-diagram.html&lt;/code&gt; file at the root of your project in your browser to explore the web of your architecture.&lt;/p&gt;

&lt;p&gt;You can also pass additional options to filter the nodes according to your needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;maxNodes=20&lt;/code&gt;&lt;/strong&gt;\
Filters for classes with the highest number of incoming connections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;basePackages=com.mycompany&lt;/code&gt;&lt;/strong&gt;\
Forces the target base package&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;includeTests=true&lt;/code&gt;&lt;/strong&gt;\
Also include test classes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To learn more 👉 &lt;a href="https://github.com/jtama/project-graph-generator" rel="noopener noreferrer"&gt;Here is the project repository&lt;/a&gt;. Go ahead, it’s open source, use it, fork it, make issues and pull requests!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foswdgbdxowhh4oeounsq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foswdgbdxowhh4oeounsq.gif" alt="please" width="320" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mechanics: Analysis Without Modification
&lt;/h2&gt;

&lt;p&gt;Under the hood, the tool is based on the &lt;strong&gt;ScanningRecipe&lt;/strong&gt; concept we saw previously. The major difference here is that the &lt;strong&gt;generation&lt;/strong&gt; phase generates &lt;strong&gt;&lt;em&gt;HTML&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;, and the *modification&lt;/em&gt;* phase (&lt;code&gt;visit&lt;/code&gt;) does nothing, nada, zilch. The &lt;del&gt;entire goal&lt;/del&gt; of the recipe is concentrated in the first pass: scanning the &lt;strong&gt;LST&lt;/strong&gt;. Well, no, not the goal, rather the intelligence (not the A.I. kind).&lt;/p&gt;

&lt;p&gt;It all starts with an accumulator - our famous graph - which will store the classes (Nodes) and their relationships (Links) as the scan progresses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GraphScanAccumulator&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt; &lt;span class="c1"&gt;// ①&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;links&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// ... search methods findNode() and findLink()&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Did you seriously think I was going to explain this line?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To fill this graph, we will traverse the &lt;strong&gt;LST&lt;/strong&gt; using a &lt;code&gt;JavaIsoVisitor&lt;/code&gt;.&lt;br&gt;
First, we identify each component of our architecture by overriding the &lt;code&gt;visitClassDeclaration&lt;/code&gt; method. Each new class encountered becomes a "Node":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ClassDeclaration&lt;/span&gt; &lt;span class="nf"&gt;visitClassDeclaration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ClassDeclaration&lt;/span&gt; &lt;span class="n"&gt;classDecl&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;includeTests&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isTestClass&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getCursor&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// ①&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classDecl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getType&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;fqn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;classDecl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getType&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getFullyQualifiedName&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findNode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fqn&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;orElseGet&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// ②&lt;/span&gt;
                &lt;span class="nc"&gt;Node&lt;/span&gt; &lt;span class="n"&gt;newNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fqn&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;classDecl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getType&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getPackageName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
                &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nodes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newNode&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;newNode&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;});&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visitClassDeclaration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classDecl&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ③&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;classDecl&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;We only look at test classes if explicitly requested.&lt;/li&gt;
&lt;li&gt;We use our &lt;code&gt;graph&lt;/code&gt; accumulator to register the current class if it hasn’t been seen already.&lt;/li&gt;
&lt;li&gt;We don’t forget to call &lt;code&gt;super&lt;/code&gt; so that the visitor continues to descend into the tree. &lt;strong&gt;&lt;em&gt;If and only if&lt;/em&gt;&lt;/strong&gt; the class is of interest to us, otherwise, we don’t waste our time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next, we must weave the web. How do we know that class &lt;code&gt;A&lt;/code&gt; depends on class &lt;code&gt;B&lt;/code&gt;? We simply capture type usages (method calls, fields access, instantiations). For example, here’s how we proceed for method invocations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodInvocation&lt;/span&gt; &lt;span class="nf"&gt;visitMethodInvocation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodInvocation&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visitMethodInvocation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ①&lt;/span&gt;

    &lt;span class="nc"&gt;JavaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FullyQualified&lt;/span&gt; &lt;span class="n"&gt;targetType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMethodType&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMethodType&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getDeclaringType&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ②&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;targetType&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;addLink&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;targetType&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ③&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Let the visitor visit.&lt;/li&gt;
&lt;li&gt;Extract the type information (&lt;code&gt;JavaType&lt;/code&gt;) carried by the invoked method to know which class it belongs to. The type can be &lt;code&gt;null&lt;/code&gt;, particularly if OpenRewrite failed to determine it.&lt;/li&gt;
&lt;li&gt;Invoke the &lt;code&gt;addLink&lt;/code&gt; method.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;addLink&lt;/code&gt; method contains all the logic allowing us to determine if the &lt;code&gt;targetType&lt;/code&gt; interests us, that is to say if it is part of the target packages. If so, we create a new link or strengthen an existing one.&lt;/p&gt;

&lt;p&gt;The same logic is obviously applied &lt;a href="https://github.com/jtama/project-graph-generator/blob/main/src/main/java/io/github/jtama/openrewrite/ProjectAerialViewGenerator.java#L118" rel="noopener noreferrer"&gt;to member references, class fields, constructor invocations, etc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is the beauty of OpenRewrite’s model: the LST is already perfectly typed by the compiler during &lt;em&gt;parsing&lt;/em&gt;. We don’t need to guess who an invoked method belongs to, the type information tells us with certainty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going Further: Raw Export with Datatables
&lt;/h2&gt;

&lt;p&gt;Generating an HTML page is nice. But what if you want to cross-reference this data, render it yourself in a tool like &lt;a href="https://gephi.org/" rel="noopener noreferrer"&gt;Gephi&lt;/a&gt;, or even provide it as context to an LLM to audit your architecture?&lt;/p&gt;

&lt;p&gt;This is where OpenRewrite’s &lt;a href="https://docs.openrewrite.org/authoring-recipes/data-tables" rel="noopener noreferrer"&gt;Datatables&lt;/a&gt; come in. In addition to the view, &lt;code&gt;project-graph-generator&lt;/code&gt; can export metadata in the form of easily usable CSV files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;*&lt;code&gt;target/rewrite/datatables/io.github.jtama.openrewrite.model.NodesReport.csv&lt;/code&gt; *\
Contains all the classes found with their package and their number of incoming/outgoing connections.&lt;/li&gt;
&lt;li&gt;*&lt;code&gt;target/rewrite/datatables/io.github.jtama.openrewrite.model.LinksReport.csv&lt;/code&gt; *\
Exhaustive list of links between classes with an associated weight.&lt;/li&gt;
&lt;li&gt;*&lt;code&gt;target/rewrite/datatables/io.github.jtama.openrewrite.model.JavaSourceFileExcludedReport.csv&lt;/code&gt; *\
All classes that were excluded from the final result (often due to package filtering).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, it’s your turn. Run the scanner on your &lt;em&gt;legacy&lt;/em&gt;, and contemplate (or be frightened by) the extent of the web!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt;: As you may have noticed, this plugin is distributed on Maven Central (&lt;code&gt;io.github.jtama:project-graph-generator&lt;/code&gt;). If you’re wondering how to easily automate the entire publication chain without tearing your hair out, I recommend you take a look at &lt;a href="https://jtama.github.io/posts/en/release-everything-with-jreleaser/" rel="noopener noreferrer"&gt;my JReleaser tutorial&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>jreleaser</category>
      <category>openrewrite</category>
      <category>graph</category>
    </item>
    <item>
      <title>Release everything with JReleaser</title>
      <dc:creator>Kosmik</dc:creator>
      <pubDate>Fri, 19 Dec 2025 13:03:32 +0000</pubDate>
      <link>https://forem.com/onepoint/release-everything-with-jreleaser-5a97</link>
      <guid>https://forem.com/onepoint/release-everything-with-jreleaser-5a97</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://jreleaser.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;JReleaser&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The quick and effortless way to release your project!&lt;/p&gt;

&lt;p&gt;Java, Go, Node, Rust, Zig, Swift, Perl, Python, C/C++, C#, Elixir, Haskell, Ruby, Crystal, and more.&lt;/p&gt;

&lt;p&gt;— Official tag line&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;JReleaser is a tool that performs the most common steps related to releasing your application.&lt;/p&gt;

&lt;p&gt;Releasing is not just about publishing a package to an artifact repository. It could also mean signing and generating artifacts, assembling and publishing a changelog, announcing the release on your favorite hub, and even making some noise about it through mail, Zulip, or whatever you want.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmk6623k55o7cxa3dcnuh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmk6623k55o7cxa3dcnuh.png" alt="JReleaser workflow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, we will focus on releasing a Java application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, it's not just Maven 🫘!
&lt;/h2&gt;

&lt;p&gt;Each packager, each artifact repository has its own specificity.&lt;/p&gt;

&lt;p&gt;As &lt;em&gt;Maven Central&lt;/em&gt; is the ultimate goal for Java developers, and as it’s also one of the most complicated repositories, this is the one we will be looking at in this blog post. But of course, once you’ve done that, everything else will be a piece of cake 🧁!&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-requisite (tools ⚒️ and credentials 🔐)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  PGP
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Generate your PGP key pair
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;gpg --generate-key
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  That will prompt the following
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
&lt;/span&gt;&lt;span class="gp"&gt;disks) during the prime generation;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;this gives the random number
&lt;span class="go"&gt;generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
&lt;/span&gt;&lt;span class="gp"&gt;disks) during the prime generation;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;this gives the random number
&lt;span class="go"&gt;generator a better chance to gain enough entropy.
gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: directory '/root/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/root/.gnupg/openpgp-revocs.d/4C2428BC373276273A30B9D4FA30C263300AD893.rev'
public and secret key created and signed.

pub   ed25519 2025-11-18 [SC] [expires: 2028-11-17]
&lt;/span&gt;&lt;span class="gp"&gt;      4C2428BC373276273A30B9D4FA30C263300AD893 &amp;lt;1&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;uid                      Guess who's back ? &amp;lt;back_again@shady.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;sub   cv25519 2025-11-18 [E] [expires: 2028-11-17]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;This is your key identifier; keep it somewhere.
&lt;/li&gt;
&lt;/ol&gt;




&lt;/p&gt;
&lt;p&gt;You’ll be asked for a passphrase to protect your keypair. Choose wisely, and don’t tell anyone&lt;/p&gt;

&lt;p&gt;If you forget to save your key identifier, you can find it back using the &lt;code&gt;list-keys&lt;/code&gt; command :&lt;/p&gt;

&lt;p&gt;
  just show me !
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;gpg &lt;span class="nt"&gt;--list-keys&lt;/span&gt;
&lt;span class="go"&gt;/root/.gnupg/pubring.kbx
------------------------
pub   ed25519 2025-11-18 [SC] [expires: 2028-11-17]
      4C2428BC373276273A30B9D4FA30C263300AD893
&lt;/span&gt;&lt;span class="gp"&gt;uid           [ultimate] Guess who's back ? &amp;lt;back_again@shady.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;sub   cv25519 2025-11-18 [E] [expires: 2028-11-17]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;h4&gt;
  
  
  Distribute it 🔑
&lt;/h4&gt;

&lt;p&gt;There are sereval public keyservers and amongst them the following are supported &lt;code&gt;keyserver.ubuntu.com&lt;/code&gt;, &lt;code&gt;keys.openpgp.org&lt;/code&gt;, &lt;code&gt;pgp.mit.edu&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The following command will make your key publicly available :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;gpg --keyserver keyserver.ubuntu.com -send-key 4C2428BC373276273A30B9D4FA30C263300AD893&amp;lt;1&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Replace with your own key&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Obtaining your maven central credentials.
&lt;/h3&gt;

&lt;p&gt;Login to &lt;a href="https://central.sonatype.com/" rel="noopener noreferrer"&gt;maven central&lt;/a&gt;, click on the top right menu item "View User Tokens", and generate a new token.&lt;/p&gt;

&lt;p&gt;You can specify its name and expiration date. Personally, I chose to go crazy for this :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frp9binxldwi7qtbn6e3p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frp9binxldwi7qtbn6e3p.png" alt="The name of the token is JReleaser"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Install JReleaser
&lt;/h3&gt;

&lt;p&gt;There are many ways &lt;a href="https://jreleaser.org/guide/latest/install.html" rel="noopener noreferrer"&gt;to install JReleaser&lt;/a&gt;, but my goto for local environment is to use &lt;a href="https://www.jbang.dev/" rel="noopener noreferrer"&gt;JBang&lt;/a&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;// Download, and install localy
jbang app install jreleaser@jreleaser

// Execute by using
&lt;/span&gt;&lt;span class="gp"&gt;jreleaser &amp;lt;command&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&amp;lt;args&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you prefere to run it without installing it, you can also use JBang :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;// Download, cache, and run
&lt;/span&gt;&lt;span class="gp"&gt;jbang jreleaser@jreleaser &amp;lt;command&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&amp;lt;args&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify your project descriptor
&lt;/h3&gt;

&lt;p&gt;Even though JReleaser will do it for you, you can always independently verify if your project descriptor has all the mandatory fields to be published by running :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;jbang pomchecker@kordamp check-maven-central
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please correct your descriptor accordingly and repeat the operation until everything is clean&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure your project
&lt;/h2&gt;

&lt;p&gt;This is a 2 steps process. You need to configure JReleaser but also your build process to generate all the necessary artefacts for publication.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Maven configuration
&lt;/h3&gt;

&lt;p&gt;Add a release profile to your &lt;code&gt;pom.xml&lt;/code&gt; :&lt;/p&gt;

&lt;p&gt;
  release profile
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;release&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;altDeploymentRepository&amp;gt;&lt;/span&gt;
            local::file:./target/staging-deploy ①
        &lt;span class="nt"&gt;&amp;lt;/altDeploymentRepository&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;defaultGoal&amp;gt;&lt;/span&gt;deploy&lt;span class="nt"&gt;&amp;lt;/defaultGoal&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-javadoc-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;attach-javadocs&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;jar&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;attach&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/attach&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-source-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;attach-sources&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;jar&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;attach&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/attach&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;You can of course change this value&lt;/li&gt;
&lt;/ol&gt;



&lt;/p&gt;

&lt;h3&gt;
  
  
  The Gradle configuration
&lt;/h3&gt;

&lt;p&gt;If you do use Gradle, I’m sure you know how to produce the equivalent configuration. I don’t.&lt;/p&gt;

&lt;h3&gt;
  
  
  The JReleaser configuration
&lt;/h3&gt;

&lt;p&gt;You can initialize your project configuration with the following command :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;jbang jreleaser@jreleaser init
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate your project configuration file that you will have to amend following your needs.&lt;/p&gt;

&lt;p&gt;
  jreleaser.yaml
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Generated with JReleaser 1.21.0 at 2025-11-19T09:09:25.55515446Z&lt;/span&gt;
&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0-SNAPSHOT&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Awesome App&lt;/span&gt;
  &lt;span class="na"&gt;longDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Awesome App&lt;/span&gt;
  &lt;span class="na"&gt;authors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Duke&lt;/span&gt;
  &lt;span class="na"&gt;license&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Apache-2.0&lt;/span&gt;
  &lt;span class="na"&gt;links&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;homepage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://acme.com/app&lt;/span&gt;
  &lt;span class="na"&gt;languages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;java&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;groupId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.acme&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;
  &lt;span class="na"&gt;inceptionYear&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2025&lt;/span&gt;

&lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;github&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;duke&lt;/span&gt;

&lt;span class="na"&gt;distributions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path/to/{{distributionName}}-{{projectVersion}}.zip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;distributions&lt;/code&gt; and &lt;code&gt;github&lt;/code&gt; can be removed if you only want &lt;code&gt;maven&lt;/code&gt; deployment, anyway you have to specify the maven deployment configuration by adding the following to your config file :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;signing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;①&lt;/span&gt;
  &lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ALWAYS&lt;/span&gt;
  &lt;span class="s"&gt;armored&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;②&lt;/span&gt;
  &lt;span class="s"&gt;maven&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;mavenCentral&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;release-deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RELEASE&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://central.sonatype.com/api/v1/publisher&lt;/span&gt;
        &lt;span class="na"&gt;stagingRepositories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;target/staging-deploy ③&lt;/span&gt;
    &lt;span class="na"&gt;nexus2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;snapshot-deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SNAPSHOT&lt;/span&gt;
        &lt;span class="na"&gt;snapshotUrl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://central.sonatype.com/repository/maven-snapshots/&lt;/span&gt;
        &lt;span class="na"&gt;applyMavenCentralRules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;snapshotSupported&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;closeRepository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;releaseRepository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;stagingRepositories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;target/staging-deploy ③&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Lets JRelease do the artefact signing for you&lt;/li&gt;
&lt;li&gt;Which maven repository to use for &lt;strong&gt;release&lt;/strong&gt; and &lt;strong&gt;snapshot&lt;/strong&gt; deployments&lt;/li&gt;
&lt;li&gt;Where to find the artefacts locally. Must match the value used in your project configuration.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will have to specify a few secret variables, but for obvious security reasons, NOT in a versioned source file...&lt;/p&gt;

&lt;h2&gt;
  
  
  Specifying variables
&lt;/h2&gt;

&lt;p&gt;Most of the JReleaser needed credentials are gonna be common for all your project. So setting them globally for JReleaser is a good thing to do. I set them in the JReleaser defaut folder. By default &lt;code&gt;$HOME/.jreleaser/config.properties&lt;/code&gt; (accepted formats are &lt;code&gt;PROPERTIES&lt;/code&gt;, &lt;code&gt;YAML&lt;/code&gt;, &lt;code&gt;TOML&lt;/code&gt; and &lt;code&gt;JSON&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jreleaser.org/guide/latest/reference/environment.html#_env_files" rel="noopener noreferrer"&gt;For more on this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I choose &lt;code&gt;TOML&lt;/code&gt; and here what my file looks like :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;~/.jreleaser/config.toml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;JRELEASER_GPG_PUBLIC_KEY&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""-----BEGIN PGP PUBLIC KEY BLOCK-----

&amp;lt;place_your_public_key_here&amp;gt;
-----END PGP PUBLIC KEY BLOCK-----"""&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
&lt;span class="py"&gt;JRELEASER_GPG_SECRET_KEY&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""-----BEGIN PGP PRIVATE KEY BLOCK-----

&amp;lt;place_your_secret_key_here&amp;gt;
-----END PGP PRIVATE KEY BLOCK-----"""&lt;/span&gt;

&lt;span class="py"&gt;JRELEASER_DRAFT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="err"&gt;②&lt;/span&gt;
&lt;span class="py"&gt;JRELEASER_GPG_PASSPHRASE&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"❤️ Eminem&amp;lt;3&amp;lt;3"&lt;/span&gt;
&lt;span class="py"&gt;JRELEASER_MAVENCENTRAL_RELEASE_DEPLOY_USERNAME&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"redacted"&lt;/span&gt;
&lt;span class="py"&gt;JRELEASER_MAVENCENTRAL_RELEASE_DEPLOY_TOKEN&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"redacted"&lt;/span&gt;
&lt;span class="py"&gt;JRELEASER_NEXUS2_SNAPSHOT_DEPLOY_USERNAME&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"redacted"&lt;/span&gt;
&lt;span class="py"&gt;JRELEASER_NEXUS2_SNAPSHOT_DEPLOY_TOKEN&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"redacted"&lt;/span&gt; &lt;span class="err"&gt;③&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Toml supports multiline string using triple double quotes: &lt;code&gt;"""&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;I only do draft release from local env&lt;/li&gt;
&lt;li&gt;Values obtained in obtaining-your-maven-central-credentials chapter. They are the same for &lt;strong&gt;maven central&lt;/strong&gt; and &lt;strong&gt;nexus2_snapshot&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One more thing, please. I prefere to only do dry run execution locally but that cannot be specified through file at the moment, so I export the value just before running the commands :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;export JRELEASER_DRY_RUN=true &amp;amp;&amp;amp; jreleaser full-release
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you’re happy, you can possibly unset the dry run property and relaunch, but I’d rather do the proper release from my &lt;em&gt;&lt;strong&gt;&lt;em&gt;CI&lt;/em&gt;&lt;/strong&gt;&lt;/em&gt;, which is Github for me at the moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running all this from Github actions
&lt;/h2&gt;

&lt;p&gt;Here is the minimal github workflow to release a maven application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Release&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;①&lt;/span&gt;
    &lt;span class="s"&gt;inputs&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;②&lt;/span&gt;
        &lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Release&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;version"&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;precheck&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;artefacts'&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Check&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;out&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;repository'&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ needs.precheck.outputs.HEAD }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Set&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;up&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Java'&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-java@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;distribution&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;temurin&lt;/span&gt;
          &lt;span class="na"&gt;java-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;21&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;maven'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;artefacts'&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mvn -B -P release clean deploy&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to Central&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jreleaser/release-action@v2 ③&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest&lt;/span&gt;
          &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;full-release'&lt;/span&gt; &lt;span class="s"&gt;④&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;JRELEASER_GPG_PUBLIC_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GPG_PUBLIC_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;JRELEASER_GPG_SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GPG_PRIVATE_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;JRELEASER_GPG_PASSPHRASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GPG_PASSPHRASE }}&lt;/span&gt;
          &lt;span class="na"&gt;JRELEASER_MAVENCENTRAL_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SONATYPE_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;JRELEASER_MAVENCENTRAL_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SONATYPE_PASSWORD }}&lt;/span&gt;
          &lt;span class="na"&gt;JRELEASER_NEXUS2_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SONATYPE_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;JRELEASER_NEXUS2_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SONATYPE_PASSWORD }} ⑤&lt;/span&gt;
          &lt;span class="na"&gt;JRELEASER_PROJECT_VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.inputs.version }} ②&lt;/span&gt;
         &lt;span class="c1"&gt;#JRELEASER_DRAFT: true ⑥&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Manually triggered workflow&lt;/li&gt;
&lt;li&gt;The version to be released. It could also be retrieved from your maven project.&lt;/li&gt;
&lt;li&gt;Uses the JReleaser github action (Integration also exists with Gitlab, Gitea and many others)&lt;/li&gt;
&lt;li&gt;Runs the &lt;strong&gt;full-release&lt;/strong&gt; workflow&lt;/li&gt;
&lt;li&gt;All the values to be assign in your project secrets&lt;/li&gt;
&lt;li&gt;Wether or not do you want draft releases.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;We’ve only scratch the surface of what can be done with JReleaser. If you want a really full example, &lt;a href="https://jreleaser.org/guide/latest/examples/jreleaser.html" rel="noopener noreferrer"&gt;here’s how JReleaser releases itself&lt;/a&gt;, with JReleaser of course.&lt;/p&gt;

&lt;p&gt;But at least you know how to publish your artefacts to Maven Central 🗻 \o/ .&lt;/p&gt;

</description>
      <category>java</category>
      <category>jreleaser</category>
      <category>maven</category>
    </item>
    <item>
      <title>Technique Avancée d'OpenRewrite : Orchestrer des refactorings complexes avec les `ScanningRecipe`</title>
      <dc:creator>Kosmik</dc:creator>
      <pubDate>Wed, 29 Oct 2025 08:59:38 +0000</pubDate>
      <link>https://forem.com/onepoint/technique-avancee-dopenrewrite-orchestrer-des-refactorings-complexes-avec-les-scanningrecipe-2onf</link>
      <guid>https://forem.com/onepoint/technique-avancee-dopenrewrite-orchestrer-des-refactorings-complexes-avec-les-scanningrecipe-2onf</guid>
      <description>&lt;dl&gt;
&lt;dt&gt;&lt;strong&gt;⚠️ WARNING&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;

Le sujet qui va être traité est avancé. En cas de doute, je vous conseille d’aller lire les articles précédents de cette série.
&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Nous avons déjà vu dans les articles précédents pas mal d'exemples. Mais chacun d'eux concerne peuvent-être traités en ne prenant en compte qu'un fichier source, une &lt;em&gt;compilation unit&lt;/em&gt; en bon &lt;em&gt;Openrewrite&lt;/em&gt;. Alors qu'allons-nous devenir si notre besoin dépasse les frontières d'un seul fichier ?&lt;/p&gt;

&lt;p&gt;Oui, mais si ma recette a besoin d'&lt;strong&gt;analyser&lt;/strong&gt; le contenu de la classe &lt;code&gt;A&lt;/code&gt; pour décider de &lt;strong&gt;modifier&lt;/strong&gt; la classe &lt;code&gt;B&lt;/code&gt; &lt;strong&gt;&lt;em&gt;et&lt;/em&gt;&lt;/strong&gt; de &lt;strong&gt;générer&lt;/strong&gt; une toute nouvelle classe &lt;code&gt;C&lt;/code&gt; ?&lt;/p&gt;

&lt;p&gt;C’est précisément le cas d’usage où les recettes classiques, qui traitent les fichiers de manière isolée, ne suffisent plus. Pour ces scénarios, OpenRewrite fournit un outil spécifique : la &lt;code&gt;ScanningRecipe&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Une &lt;code&gt;ScanningRecipe&lt;/code&gt; découpe le refactoring en trois phases distinctes :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Une phase d’analyse (&lt;code&gt;scan&lt;/code&gt;)&lt;/em&gt;&lt;/strong&gt; : Un premier visiteur parcourt &lt;strong&gt;&lt;em&gt;tous&lt;/em&gt;&lt;/strong&gt; les fichiers sources pour collecter des informations, sans rien modifier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Une phase de génération (&lt;code&gt;generate&lt;/code&gt;)&lt;/em&gt;&lt;/strong&gt; : À partir des informations collectées, cette étape peut créer de nouveaux fichiers sources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Une phase de modification (&lt;code&gt;visit&lt;/code&gt;)&lt;/em&gt;&lt;/strong&gt; : Un second visiteur parcourt à nouveau les fichiers pour appliquer les modifications, en s’aidant des données de la phase d’analyse.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Cas d'usage : Extraire une interface
&lt;/h2&gt;

&lt;p&gt;Pour illustrer ce processus, nous allons implémenter une recette qui extrait une interface de toutes les classes annotées avec &lt;code&gt;@LearnToFly&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pour une classe comme celle-ci :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.foo.fighter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;//imports&lt;/span&gt;

&lt;span class="nd"&gt;@LearnToFly&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RocketController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Operation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Launches a rocket to a given destination"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Check availability and other constraints before doing anything."&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@ApiResponses&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;@ApiResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;responseCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"200"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Successfully launched"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="nd"&gt;@ApiResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;responseCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"404"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Not found - The destination isn't in the solar system"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/rocket/{destination}"&lt;/span&gt;&lt;span class="o"&gt;)&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Beaucoup, beaucoup trop d’annotations qui nuisent à la lisibilité du code&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;La recette doit :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Trouver&lt;/em&gt;&lt;/strong&gt; la classe &lt;code&gt;RocketController&lt;/code&gt; grâce à son annotation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Générer&lt;/em&gt;&lt;/strong&gt; une nouvelle interface &lt;code&gt;IRocketController&lt;/code&gt; dans un module différent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Modifier&lt;/em&gt;&lt;/strong&gt; &lt;code&gt;RocketController&lt;/code&gt; pour qu’elle implémente &lt;code&gt;IRocketController&lt;/code&gt;, et supprimer l’annotation &lt;code&gt;@LearnToFly&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Analysons l’implémentation (disponible sur &lt;a href="https://github.com/jtama/openrewrite-refactoring-as-code" rel="noopener noreferrer"&gt;le dépôt d’exemples&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 1 : La collecte d'informations
&lt;/h2&gt;

&lt;p&gt;La première étape est une mission de reconnaissance. Le &lt;code&gt;Scanner&lt;/code&gt; parcourt le code source pour identifier les classes cibles et stocke les informations dans un &lt;code&gt;Accumulator&lt;/code&gt;, un conteneur de données partagé entre les phases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExtractInterface&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ScanningRecipe&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ExtractInterface&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Accumulator&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Accumulator&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
        &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ToExtract&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toBeExtracted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Accumulator&lt;/span&gt; &lt;span class="nf"&gt;getInitialValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExecutionContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Accumulator&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;TreeVisitor&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?,&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getScanner&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Accumulator&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JavaIsoVisitor&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ExecutionContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;@Override&lt;/span&gt;
            &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ClassDeclaration&lt;/span&gt; &lt;span class="nf"&gt;visitClassDeclaration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ClassDeclaration&lt;/span&gt; &lt;span class="n"&gt;classDecl&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isClass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classDecl&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;alreadyImplementsInterface&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classDecl&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt;②&lt;/span&gt;
                    &lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toBeExtracted&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classDecl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getType&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getFullyQualifiedName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ToExtract&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classDecl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSourcePath&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;...));&lt;/span&gt; &lt;span class="err"&gt;③&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;classDecl&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;};&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;L'&lt;code&gt;Accumulator&lt;/code&gt; est une simple classe statique interne qui sert de conteneur pour les données que nous collectons.&lt;/li&gt;
&lt;li&gt;On vérifie que la classe est bien celle ciblée par l’annotation &lt;code&gt;@LearnToFly&lt;/code&gt; et qu’elle n’implémente pas déjà l’interface.&lt;/li&gt;
&lt;li&gt;Si la classe correspond, on stocke les informations nécessaires (comme son &lt;code&gt;sourcePath&lt;/code&gt;) dans l'&lt;code&gt;Accumulator&lt;/code&gt;. Le &lt;em&gt;FQDN&lt;/em&gt; de la classe sert de clé.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;À la fin de cette phase, aucune modification n’a été faite, mais notre &lt;code&gt;Accumulator&lt;/code&gt; contient une "liste de tâches" de toutes les classes à traiter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 2 : La génération de l'interface
&lt;/h2&gt;

&lt;p&gt;Cette méthode est exécutée après le &lt;code&gt;Scanner&lt;/code&gt;. Elle utilise les données de l'&lt;code&gt;Accumulator&lt;/code&gt; pour construire de nouveaux &lt;code&gt;SourceFile&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SourceFile&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Accumulator&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toBeExtracted&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toExtract&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// ... logique pour déterminer le chemin de la nouvelle interface ...&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SourceFile&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;getExtractedInterface&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toExtract&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withSourcePath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newInterfacePath&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withMarkers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Tree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;randomId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;})&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CompilationUnit&lt;/span&gt; &lt;span class="nf"&gt;getExtractedInterface&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ToExtract&lt;/span&gt; &lt;span class="n"&gt;toExtract&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;JavaTemplate&lt;/span&gt; &lt;span class="n"&gt;interfaceTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JavaTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"public interface #{any(String)} {}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CompilationUnit&lt;/span&gt; &lt;span class="n"&gt;anInterface&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CompilationUnit&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JavaIsoVisitor&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;@Override&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodDeclaration&lt;/span&gt; &lt;span class="nf"&gt;visitMethodDeclaration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodDeclaration&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withBody&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="err"&gt;②&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nd"&gt;@Override&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;VariableDeclarations&lt;/span&gt; &lt;span class="nf"&gt;visitVariableDeclarations&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;VariableDeclarations&lt;/span&gt; &lt;span class="n"&gt;multiVariable&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="err"&gt;③&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}.&lt;/span&gt;&lt;span class="na"&gt;visit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toExtract&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOriginalTree&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;anInterface&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Pour chaque classe à traiter, on appelle une méthode qui va fabriquer la structure de notre nouvelle interface.&lt;/li&gt;
&lt;li&gt;La transformation principale se fait via un petit visiteur interne. Pour chaque méthode, on supprime son corps avec &lt;code&gt;withBody(null)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On supprime également toutes les déclarations de variables en retournant &lt;code&gt;null&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Le résultat est une collection de &lt;code&gt;J.CompilationUnit&lt;/code&gt; qui représentent nos nouvelles interfaces, prêtes à être ajoutées au projet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 3 : La modification des classes existantes
&lt;/h2&gt;

&lt;p&gt;Dernière étape : modifier les classes originales. Pour cela, on réutilise la technique des messages. On parcourt les &lt;code&gt;ClassDeclaration&lt;/code&gt; et si l’une d’elles correspond à une entrée de notre &lt;code&gt;Accumulator&lt;/code&gt;, on lui attache un message.&lt;/p&gt;

&lt;h3&gt;
  
  
  La déclaration de la classe
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;TreeVisitor&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?,&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getVisitor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Accumulator&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JavaIsoVisitor&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ExecutionContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;@Override&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ClassDeclaration&lt;/span&gt; &lt;span class="nf"&gt;visitClassDeclaration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ClassDeclaration&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;ToExtract&lt;/span&gt; &lt;span class="n"&gt;toExtract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toBeExtracted&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getType&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getFullyQualifiedName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toExtract&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;getCursor&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;putMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TARGET_CLASS&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toExtract&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;annotations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLeadingAnnotations&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="n"&gt;annotations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;removeIf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ann&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;targetAnnotationMatcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matches&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ann&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
                &lt;span class="n"&gt;maybeRemoveImport&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;targetAnnotation&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="err"&gt;②&lt;/span&gt;
                &lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withLeadingAnnotations&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;annotations&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withImplements&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TypeTree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;extractedInterfaceName&lt;/span&gt;&lt;span class="o"&gt;())));&lt;/span&gt; &lt;span class="err"&gt;③&lt;/span&gt;
                &lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visitClassDeclaration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;executionContext&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visitClassDeclaration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;};&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Si la &lt;code&gt;ClassDeclaration&lt;/code&gt; en cours de visite est l’une de nos cibles, on attache les données la concernant (&lt;code&gt;toExtract&lt;/code&gt;) comme message sur le curseur.&lt;/li&gt;
&lt;li&gt;Dans &lt;code&gt;visitClassDeclaration&lt;/code&gt;, on vérifie si un message est présent. Si oui, on sait qu’on doit modifier cette classe.&lt;/li&gt;
&lt;li&gt;On ajoute la clause &lt;code&gt;implements&lt;/code&gt; et on supprime l’annotation &lt;code&gt;@LearnToFly&lt;/code&gt;. Un autre visiteur (non montré ici) s’occupe d’ajouter &lt;code&gt;@Override&lt;/code&gt; aux méthodes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Les déclarations de méthodes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodDeclaration&lt;/span&gt; &lt;span class="nf"&gt;visitMethodDeclaration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodDeclaration&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&lt;/span&gt; &lt;span class="n"&gt;executionContext&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getCursor&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getNearestMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;TARGET_CLASS&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;JavaTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@Override"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;apply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getCursor&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCoordinates&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;replaceAnnotations&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visitMethodDeclaration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;executionContext&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Si la classe a été marquée de sceau rouge de l’extraction, on ajoute l’annotation &lt;code&gt;@Override&lt;/code&gt; à toutes les déclarations de méthodes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Avant/Après
&lt;/h2&gt;

&lt;p&gt;Voici par exemple une classe avant extraction : &lt;/p&gt;

&lt;p&gt;
  HostelController open
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.github.jtama.app.reservation.Reservation&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.github.jtama.toxic.BigDecimalUtils&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.github.jtama.toxic.LearnToFly&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.annotation.security.RolesAllowed&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.inject.Inject&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.transaction.Transactional&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.Consumes&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.GET&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.POST&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.Path&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.PathParam&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.Produces&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.QueryParam&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.core.Context&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.core.MediaType&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.core.SecurityContext&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.List&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/hostels"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Produces&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@LearnToFly&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HostelController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Inject&lt;/span&gt;
    &lt;span class="nc"&gt;HostelReservationService&lt;/span&gt; &lt;span class="n"&gt;hostelReservationService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Hostel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getAll&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Hostel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@POST&lt;/span&gt;
    &lt;span class="nd"&gt;@Consumes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@RolesAllowed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ADMIN"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Transactional&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Hostel&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Hostel&lt;/span&gt; &lt;span class="n"&gt;hostel&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Hostel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;persistIfNotExists&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hostel&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@POST&lt;/span&gt;
    &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/{name}/book"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@RolesAllowed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"USER"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Reservation&lt;/span&gt; &lt;span class="nf"&gt;book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@QueryParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"month"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@Context&lt;/span&gt; &lt;span class="nc"&gt;SecurityContext&lt;/span&gt; &lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;BigDecimalUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;longValue&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;&lt;span class="c1"&gt;// Because I can !&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hostelReservationService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUserPrincipal&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;L'interface extraite : &lt;/p&gt;

&lt;p&gt;
  IHostelController open
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.github.jtama.app.reservation.Reservation&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.github.jtama.toxic.LearnToFly&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.annotation.security.RolesAllowed&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.transaction.Transactional&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.Consumes&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.GET&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.POST&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.Path&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.PathParam&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.Produces&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.QueryParam&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.core.Context&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.core.MediaType&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.core.SecurityContext&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.List&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/hostels"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Produces&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@LearnToFly&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IHostelController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Hostel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;@POST&lt;/span&gt;
    &lt;span class="nd"&gt;@Consumes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@RolesAllowed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ADMIN"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Transactional&lt;/span&gt; &lt;span class="nc"&gt;Hostel&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Hostel&lt;/span&gt; &lt;span class="n"&gt;hostel&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@POST&lt;/span&gt;
    &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/{name}/book"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@RolesAllowed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"USER"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Reservation&lt;/span&gt; &lt;span class="nf"&gt;book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@QueryParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"month"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@Context&lt;/span&gt; &lt;span class="nc"&gt;SecurityContext&lt;/span&gt; &lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;Et la classe après extraction : &lt;/p&gt;

&lt;p&gt;
  IHostelController open
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.github.jtama.app.reservation.Reservation&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.inject.Inject&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.PathParam&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.QueryParam&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.core.Context&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.core.SecurityContext&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.math.BigDecimal&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.List&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HostelController&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;IHostelController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Inject&lt;/span&gt;
    &lt;span class="nc"&gt;HostelReservationService&lt;/span&gt; &lt;span class="n"&gt;hostelReservationService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Hostel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getAll&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Hostel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Hostel&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Hostel&lt;/span&gt; &lt;span class="n"&gt;hostel&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Hostel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;persistIfNotExists&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hostel&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Reservation&lt;/span&gt; &lt;span class="nf"&gt;book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@QueryParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"month"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@Context&lt;/span&gt; &lt;span class="nc"&gt;SecurityContext&lt;/span&gt; &lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;longValue&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;&lt;span class="c1"&gt;// Because I can !&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hostelReservationService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUserPrincipal&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;h2&gt;
  
  
  C'est dans la boîte
&lt;/h2&gt;

&lt;p&gt;J’espère vous avoir convaincu qu’il n’y a encore une fois rien de sorcier. Des concepts séparément simples qui une fois réunis nous permettent des réécritures relativement complexes.&lt;/p&gt;

&lt;p&gt;Encore une fois, vos cas d’usages sont la limite ! Je vous ferai bientôt part d’un nouveau joujou pour visualiser vos projets d’une nouvelle manières.&lt;/p&gt;

&lt;p&gt;Et comme d'habitude, tout le code est disponible dans &lt;a href="https://github.com/jtama/openrewrite-refactoring-as-code" rel="noopener noreferrer"&gt;le dépôt &lt;strong&gt;Openrewrite refactoring as code&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>openrewrite</category>
      <category>messaging</category>
    </item>
    <item>
      <title>Technique Avancée d'OpenRewrite : Utiliser les messages pour implémenter des logiques complexes</title>
      <dc:creator>Kosmik</dc:creator>
      <pubDate>Thu, 11 Sep 2025 07:05:37 +0000</pubDate>
      <link>https://forem.com/onepoint/technique-avancee-dopenrewrite-utiliser-les-messages-pour-implementer-des-logiques-complexes-55ed</link>
      <guid>https://forem.com/onepoint/technique-avancee-dopenrewrite-utiliser-les-messages-pour-implementer-des-logiques-complexes-55ed</guid>
      <description>&lt;p&gt;&lt;a href="https://www.pexels.com/photo/water-drops-on-blue-background-260551/" rel="noopener noreferrer"&gt;&lt;em&gt;Photo par Pixabay&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Je vous avais déjà parlé d’Openrewrite dans le premier article de cette série. Si vous ne l’avez pas lu et que vous avez besoin d’un petit rafraichissement, je vous invite à mettre la lecture de ce post en ⏸️ et à y revenir plus tard. C’est bon ? Allez go.&lt;/p&gt;

&lt;p&gt;Tous les cas d’usage dont il a été question dans l’article précédent font intervenir une recette qui n’a besoin que des informations trouvées à un et seul niveau du &lt;a href="https://docs.openrewrite.org/concepts-and-explanations/lossless-semantic-trees" rel="noopener noreferrer"&gt;LST&lt;/a&gt;. Puisqu’une image vaut mieux qu’un long discours, et que je sens bien que je ne me suis pas très bien fait comprendre, voici un exemple crédible d’AST:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfdo0wy6wn48n84iiwtn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfdo0wy6wn48n84iiwtn.png" alt="Exemple d'AST simplifié" width="787" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jusqu’à présent les exemples opéraient des modifications sur des invocations de méthodes, ou par exemple un renommage de classe.&lt;/p&gt;

&lt;p&gt;Dans le diagramme précédent, chaque nœud correspond à un élément d’AST (les choses sont évidemments simplifiées ici). Comme nous l’avons déjà vu, &lt;a href="https://docs.openrewrite.org/" rel="noopener noreferrer"&gt;OpenRewrite&lt;/a&gt; utilise le &lt;em&gt;pattern&lt;/em&gt; visiteur pour parcourir l’arbre. Plus précisément les recettes implémentent des visiteurs dont les interfaces correspondent au langage manipulé.&lt;br&gt;
En l’occurence, les manipulations sont effectuées sur du &lt;a href="https://dev.java/" rel="noopener noreferrer"&gt;Java&lt;/a&gt;, et le visiteur utilisé sera donc un &lt;a href="https://github.com/openrewrite/rewrite/blob/main/rewrite-java/src/main/java/org/openrewrite/java/JavaIsoVisitor.java" rel="noopener noreferrer"&gt;&lt;code&gt;JavaIsoVisitor&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Oui, mais si mon cas d’usage nécessite d’identifier une invocation de méthode pour agir sur la déclaration de la méthode qui la contient dans une classe?&lt;/p&gt;

&lt;p&gt;Oui, je te comprends, et cette question m’a moi aussi empecher de dormir pendant quelques jours. Seulement vois-tu, les gens derrières Openrewrite aussi.&lt;br&gt;
Ils nous ont donc mis à disposition un système de message très convaincant et plutôt sûr.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F47jqvm2a9it9rmljieni.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F47jqvm2a9it9rmljieni.png" alt="Philipidès courant un message à la main" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Cas d'usage
&lt;/h2&gt;

&lt;p&gt;Imaginons que quelqu’un de mal avisé, non, n’insistez pas, je ne vous dirai pas qui. D’accooooord c’est moi. Imaginons donc, disais-je, que quelqu’un de mal avisé ait développé dans une librarie un moyen d’enregistrer le traitement d’une méthode :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jtama&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toxic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logStart&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jtama&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toxic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logEnd&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jtama&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;acme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;longRunningMethod&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ManualGearCar&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Deprecated&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;drift&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;logStart&lt;/span&gt;&lt;span class="o"&gt;();&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;longRunningMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;);&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;logEnd&lt;/span&gt;&lt;span class="o"&gt;();&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Enregistre le début d’exécution de la méthode&lt;/li&gt;
&lt;li&gt;Exécute un traitement&lt;/li&gt;
&lt;li&gt;Enregistre la fin d’exécution de la méthode&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Random annotation&lt;/em&gt; qui aura son intérêt plus tard&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Arpès d’intense recherche nous avons découvert l’existence de &lt;a href="https://micrometer.io/" rel="noopener noreferrer"&gt;&lt;em&gt;&lt;strong&gt;&lt;em&gt;micrometer&lt;/em&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt; et de son annotation &lt;code&gt;io.micrometer.core.annotation.Timed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;En tant que développeur, je vais donc identifier l’invocation de la méthode &lt;code&gt;Timer#logStart()&lt;/code&gt; et si je la trouve :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La supprimer&lt;/li&gt;
&lt;li&gt;Supprimer l’invocation de la méthode &lt;code&gt;Timer#logEnd()&lt;/code&gt; si elle existe&lt;/li&gt;
&lt;li&gt;Ajouter l’annotation &lt;code&gt;@Timed&lt;/code&gt; sur la déclaration de la méthode &lt;code&gt;drift&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et c’est exactement la logique que nous allons mettre en œuvre dans la recette.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implémentation
&lt;/h2&gt;

&lt;p&gt;Dans cet exemple, nous ne regardons que l’implémentation du visiteur, pas la recette complète. Mais comme d’habitude, tout est disponible dans &lt;a href="https://github.com/jtama/openrewrite-refactoring-as-code/blob/main/toxic-library-remover/src/main/java/com/github/jtama/openrewrite/RemoveLogStartInvocations.java" rel="noopener noreferrer"&gt;le dépôt github&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReplaceCompareVisitor&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JavaIsoVisitor&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ExecutionContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;MethodMatcher&lt;/span&gt; &lt;span class="n"&gt;logStartInvocaMatcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MethodMatcher&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.github.jtama.toxic.Timer logStart()"&lt;/span&gt;&lt;span class="o"&gt;);&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;MethodMatcher&lt;/span&gt; &lt;span class="n"&gt;logEndInvocaMatcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MethodMatcher&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.github.jtama.toxic.Timer logEnd()"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;AnnotationMatcher&lt;/span&gt; &lt;span class="n"&gt;logStartMatcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AnnotationMatcher&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@io.micrometer.core.annotation.Timed"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;JavaTemplate&lt;/span&gt; &lt;span class="n"&gt;annotationTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JavaTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@Timed"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.micrometer.core.annotation.Timed"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;javaParser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="nc"&gt;JavaParser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJavaVersion&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;classpath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JavaParser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;runtimeClasspath&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ReplaceCompareVisitor&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;maybeRemoveImport&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.github.jtama.toxic.Timer"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nd"&gt;@Override&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodDeclaration&lt;/span&gt; &lt;span class="nf"&gt;visitMethodDeclaration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodDeclaration&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodDeclaration&lt;/span&gt; &lt;span class="n"&gt;md&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visitMethodDeclaration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;Cursor&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getCursor&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"appendAnnotation"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;md&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLeadingAnnotations&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;noneMatch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;logStartMatcher:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;maybeAddImport&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.micrometer.core.annotation.Timed"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                    &lt;span class="n"&gt;md&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;annotationTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCoordinates&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;addAnnotation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Comparator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;comparing&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Annotation&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;getSimpleName&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;md&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nd"&gt;@Override&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodInvocation&lt;/span&gt; &lt;span class="nf"&gt;visitMethodInvocation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodInvocation&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodInvocation&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visitMethodInvocation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logStartInvocaMatcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matches&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;logEndInvocaMatcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matches&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;getCursor&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;putMessageOnFirstEnclosing&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodDeclaration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"appendAnnotation"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Les utilitaires&lt;/li&gt;
&lt;li&gt;Le visiteur d’invocation de méthode&lt;/li&gt;
&lt;li&gt;Le visiteur de déclaration de méthode&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Les utilitaires
&lt;/h3&gt;

&lt;p&gt;Dans cette section, deux outils d’OpenRewrite sont utilisés. Le &lt;code&gt;MethodMatcher&lt;/code&gt; permet d’identifier précisément les invocations de méthodes ciblées, telles que &lt;code&gt;logStart()&lt;/code&gt; et &lt;code&gt;logEnd()&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;L' &lt;code&gt;AnnotationMatcher&lt;/code&gt; sert à vérifier la présence de l’annotation &lt;code&gt;@Timed&lt;/code&gt; sur une méthode. &lt;/p&gt;

&lt;p&gt;Enfin, le &lt;code&gt;JavaTemplate&lt;/code&gt; facilite l’insertion de l’annotation dans le code source et gère l’import nécessaire. &lt;/p&gt;

&lt;p&gt;Ces utilitaires simplifient la manipulation de l’AST et préparent les modifications à appliquer dans les visiteurs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Le visiteur d'invocation de méthode (&lt;code&gt;visitMethodInvocation&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Ce visiteur parcourt chaque appel de méthode dans le code source. &lt;/p&gt;

&lt;p&gt;Lorsqu’il rencontre une invocation de &lt;code&gt;logStart()&lt;/code&gt; ou &lt;code&gt;logEnd()&lt;/code&gt;, il ajoute un message pour signaler qu’une annotation devra être ajoutée ultérieurement.&lt;/p&gt;

&lt;p&gt;Un message n’est en réalité qu’un tuple &lt;code&gt;&amp;lt;clef;valeur&amp;gt;&lt;/code&gt;, mais ce qui est intéressant ici, c’est que l’on précise le scope d’accessibilité du message pour éviter de polluer tout le contexte.&lt;/p&gt;

&lt;p&gt;En occurrence, seul la méthode contenant l’invocation en question pourra accéder au message(&lt;code&gt;putMessageOnFirstEnclosing(J.MethodDeclaration.class...&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;La valeur &lt;code&gt;null&lt;/code&gt; renvoyée dans ce cas à pour effet la suppression du nœud en cours de visite, c’est-à-dire, l’invocation de la méthode.&lt;/p&gt;

&lt;h3&gt;
  
  
  Le visiteur de déclaration de méthode(&lt;code&gt;visitMethodDeclaration&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Celui-là est vraiment plus simple.&lt;/p&gt;

&lt;p&gt;Si on m’a passé un message ET que la méthode n’est pas déjà annoté (si le développeur voulait mettre ceinture et bretelle... ¯\&lt;em&gt;(ツ)&lt;/em&gt;/¯ ), j’ajoute l’annotation &lt;code&gt;@Timed&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  En image
&lt;/h3&gt;

&lt;p&gt;Puisqu’une image vaut mieux qu’un long discours.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5y1d5uy3r98paa7tf60.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5y1d5uy3r98paa7tf60.png" alt="Résumé de l'algorithme sous forme de diagramme" width="442" height="718"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Et après ?
&lt;/h2&gt;

&lt;p&gt;D’autres exemples complexes suivront et n’oubliez pas que tout le code est disponible dans &lt;a href="https://github.com/jtama/openrewrite-refactoring-as-code" rel="noopener noreferrer"&gt;le dépôt &lt;strong&gt;Openrewrite refactoring as code&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>openrewrite</category>
      <category>messaging</category>
    </item>
    <item>
      <title>De la Programmation Orientée Objet vers la Programmation Orientée Données - Un guide pratique</title>
      <dc:creator>Kosmik</dc:creator>
      <pubDate>Tue, 29 Jul 2025 09:11:45 +0000</pubDate>
      <link>https://forem.com/onepoint/de-la-programmation-orientee-objet-vers-la-programmation-orientee-donnees-un-guide-pratique-4bh2</link>
      <guid>https://forem.com/onepoint/de-la-programmation-orientee-objet-vers-la-programmation-orientee-donnees-un-guide-pratique-4bh2</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;La programmation orientée données (Data-Oriented Programming) représente un paradigme émergent qui privilégie la manipulation des données plutôt que l’encapsulation des comportements. Ce guide pratique explore les étapes clés pour transformer du code Java traditionnel orienté objet vers une approche orientée données, en s’appuyant sur l’exemple concret du dépôt &lt;a href="https://github.com/jtama/crazy-data-oriented-programming" rel="noopener noreferrer"&gt;https://github.com/jtama/crazy-data-oriented-programming&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Le dépôt &lt;code&gt;crazy-data-oriented-programming&lt;/code&gt; illustre cette transformation à travers l’exemple d’un système de cartes à jouer. La branche &lt;code&gt;main&lt;/code&gt; présente une implémentation orientée objet classique, tandis que la branche &lt;code&gt;expected&lt;/code&gt; montre l’évolution vers une approche orientée données utilisant les fonctionnalités modernes de Java.&lt;/p&gt;

&lt;p&gt;Il a été pensé pour être utilisé comme kata, donc si vous vous sentez de l’animer en équipe, ne vous privez pas.&lt;/p&gt;

&lt;p&gt;Il va sans dire que les exemples sont triviaux, et que pour ce cas précis un autre design aurait certainement été plus simple. C’est un exercice de style.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table des matières
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Présentation&lt;/li&gt;
&lt;li&gt;Étape 1 : Les classes scellées&lt;/li&gt;
&lt;li&gt;Étape 2 : Restructuration de la Hiérarchie&lt;/li&gt;
&lt;li&gt;Étape 3 : Utilisation de &lt;code&gt;Records&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Étape 4 : Simplification du traitement avec Pattern Matching&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;li&gt;Ressources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="presentation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Présentation
&lt;/h2&gt;

&lt;p&gt;Oui, parce qu’avant de tout retaper, on va peut-être apprendre à se connaître non ?&lt;/p&gt;

&lt;h3&gt;
  
  
  Les classes
&lt;/h3&gt;

&lt;p&gt;Un petit diagramme pour commencer :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fik5abkddiprwyirxx4ql.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fik5abkddiprwyirxx4ql.png" alt="Diagramme de classe" width="695" height="769"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nous avons donc une hiérarchie d’objets pour représenter le jeu de carte, c’est un jeu de tarot au cas où ne l’auriez pas deviné, et une classe &lt;code&gt;AdviceGiver&lt;/code&gt; qui vous donne son avis sur vos cartes.&lt;/p&gt;

&lt;p&gt;Pas de troll ici, un atout se dit bien &lt;em&gt;trump&lt;/em&gt; en anglais. ¯\&lt;em&gt;(ツ)&lt;/em&gt;/¯&lt;/p&gt;

&lt;h3&gt;
  
  
  Le code, le code, le code
&lt;/h3&gt;

&lt;p&gt;Ici, ne regardons que le code intéressant (hum) de la class &lt;code&gt;AdviceGiver&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;advice&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PlayingCard&lt;/span&gt; &lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playingCard&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;SuitCard&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
        &lt;span class="nc"&gt;SuitCard&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SuitCard&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="err"&gt;②&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;face&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt;③&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;face&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                    &lt;span class="s"&gt;" of "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                    &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"("&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;")"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                    &lt;span class="s"&gt;" is strong"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt;④&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The first of "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"("&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;") is very weak"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The second of "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"("&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;") is very weak"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The third of "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"("&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;") is very weak"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"th of "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"("&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;") is still weak"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"th of "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"("&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;") may win you a hand"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalStateException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unexpected value: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The trump n°"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" is strong"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="err"&gt;⑤&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Si la carte est de type classique&lt;/li&gt;
&lt;li&gt;Alors on la &lt;em&gt;cast&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Si ce n’est pas une figure&lt;/li&gt;
&lt;li&gt;Sinon, en fonction de son idex&lt;/li&gt;
&lt;li&gt;Sinon, c’est forcément que c’est un atout. Non ?&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Ah, mais attention, ici on joue à la galloise, avec des Jokers ! Tout de suite, c’est un peu plus drôle."&lt;br&gt;
&lt;strong&gt;Perceval&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;"Ahh mais bien sûr, on va ajouter ça."&lt;/em&gt;&lt;/strong&gt; vous dîtes vous. Vous devez donc ajouter un nouveau type de carte, et surtout, bien faire attention, parce que votre méthode &lt;code&gt;advice&lt;/code&gt; est maintenant toute pétée...&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ça serait teeeeellement bien si le compilateur pouvait nous guider...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="scellee"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 1 : Les classes scellées
&lt;/h2&gt;

&lt;p&gt;Depuis Java 17, tels les dieux qui scellèrent Excalibur dans la roche, nous pouvons figer nos hierarchies de classes. On espère juste qu’aucun hacker répondant au doux nom d’Arthur viendra faire son malin. Mais bon, je m’égare.&lt;/p&gt;

&lt;p&gt;Les classes et interfaces scellées, &lt;em&gt;sealed&lt;/em&gt; en bon anglais, permette de limiter les implémentations possibles à un ensemble fini de classes. Cet ensemble est renforcé à la compilation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avant : Interface Simple
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PlayingCard&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Après : Interface Scellée (Sealed Interface)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PlayingCard&lt;/span&gt; &lt;span class="n"&gt;permits&lt;/span&gt; &lt;span class="nc"&gt;SuitCard&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TrumpCard&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2 nouveaux mots réservés introduits ici, &lt;code&gt;sealed&lt;/code&gt;, bon voilà, on a compris, et &lt;code&gt;permits&lt;/code&gt; qui permet de lister les implémentations autorisées.&lt;/p&gt;

&lt;p&gt;On peut permettre 3 types d’enfants d’une classe/interface scellée :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Une classe/interface scellée&lt;/li&gt;
&lt;li&gt;Une classe &lt;code&gt;final&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Un record, qui est par construction &lt;code&gt;final&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;On peut aussi utiliser une classe &lt;code&gt;non-sealed&lt;/code&gt; pour réouvrir la hiérarchie. Il y a des cas qui le justifient, mais là vous êtes à nouveau sans filet. On m'a forcé à vous le dire. Moi je ne voulais pas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour résumer, la hiérarchie d’objets d’une classe/interface scellée est finie et connue à la compilation.&lt;/p&gt;

&lt;p&gt;&lt;a id="restructuration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 2 : Restructuration de la Hiérarchie
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Avant : Classe Concrète Complexe
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SuitCard&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PlayingCard&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Face&lt;/span&gt; &lt;span class="n"&gt;face&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;SuitCard&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Face&lt;/span&gt; &lt;span class="n"&gt;face&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Validation complexe avec logique conditionnelle&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Color must not be null."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Index must not be null."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IndexOutOfBoundsException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Index must be positive."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IndexOutOfBoundsException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Index must be lesser than 15."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;face&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Face must not be null for index greater than 10."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;face&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Face must be null for index lesser than 11."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;face&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;face&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Getters, equals, hashCode...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dans cet exemple, il n’y a qu’une classe pour représenter une carte de couleur, et l’énumération des figures, trop simpliste, nous impose des contrôles complexes. Vous remarquerez que ces contrôles ne sont même pas exhaustif, puisque nous ne validons pas la correspondance entre les figures et les index.&lt;/p&gt;

&lt;h3&gt;
  
  
  Évolution de l'enum &lt;code&gt;Face&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Une énumération n’est pas limité à une liste idiote de constante. Elle peut avoir des attributs et des méthodes. Ici, nous allons juste ajouter un index à l’énumération des figures, ce qui va nous permettre de simplifier le constructeur des cartes ET augmenter le contrôle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;Face&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="no"&gt;JACK&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Jack"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="no"&gt;KNIGTH&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Knight"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="no"&gt;QUEEN&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Queen"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="no"&gt;KING&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"King"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;displayName&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nc"&gt;Face&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;displayName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;displayName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;displayName&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;displayName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;displayName&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Spécialisation
&lt;/h3&gt;

&lt;p&gt;Ici, nous séparons les cartes de couleur avec et sans figure. Encore une fois pour permettre au compilateur de jouer son rôle de garde-fou, plutôt que de devoir l’implémenter nous même.&lt;/p&gt;

&lt;p&gt;En effet, chaque type encapsule ici sa propre logique et les contraintes sont exprimées par la hiérarchie de types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;SuitCard&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;PlayingCard&lt;/span&gt; &lt;span class="n"&gt;permits&lt;/span&gt; &lt;span class="nc"&gt;NumberSuitCard&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RoyalSuitCard&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Color&lt;/span&gt; &lt;span class="nf"&gt;color&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpsosn0d622m4j436xl0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpsosn0d622m4j436xl0d.png" alt="Diagramme de classe scellée" width="800" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="records"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 3 : Utilisation de &lt;code&gt;Records&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Les &lt;code&gt;record&lt;/code&gt; ont été ajouté à Java dans la version 14. Ils ont les caractéristiques suivantes :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leurs propriétés sont immutables, elles ne peuvent en tout cas pas être réaffectées.&lt;/li&gt;
&lt;li&gt;Ils sont &lt;code&gt;final&lt;/code&gt; par constructions.&lt;/li&gt;
&lt;li&gt;Leurs méthodes &lt;code&gt;toString&lt;/code&gt;, &lt;code&gt;equals&lt;/code&gt; et &lt;code&gt;hashCode&lt;/code&gt; sont automatiquements générées à partir de leurs propriétés.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cartes Numériques
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;NumberSuitCard&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;SuitCard&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;NumberSuitCard&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requireNonNull&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Color cannot be null"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requireNonNull&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Index cannot be null"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Index must be between 1 and 10 included"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les méthodes &lt;code&gt;color()&lt;/code&gt; et &lt;code&gt;index()&lt;/code&gt; sont générés par java directement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cartes Royales
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;RoyalSuitCard&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Face&lt;/span&gt; &lt;span class="n"&gt;face&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;SuitCard&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;RoyalSuitCard&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requireNonNull&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Color cannot be null"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requireNonNull&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;face&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Face cannot be null"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;face&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les méthodes &lt;code&gt;color()&lt;/code&gt; et &lt;code&gt;face()&lt;/code&gt; sont générés par java directement, et il ne nous reste plus qu’à implémenter la méthode &lt;code&gt;index&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Cartes Trump
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;TrumpCard&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PlayingCard&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;TrumpCard&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IndexOutOfBoundsException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Index must be positive and lesser than 22."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Concepts clés :
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Records&lt;/em&gt;&lt;/strong&gt; : Réduisent drastiquement le code boilerplate (environ une trentaine de ligne par classe dans notre cas)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Constructeur compact&lt;/em&gt;&lt;/strong&gt; : Validation des données à la construction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Immutabilité&lt;/em&gt;&lt;/strong&gt; : Les données sont immuables par défaut, ou pour être plus précis, les attributs sont &lt;code&gt;final&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="patternmatching"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Étape 4 : Simplification du traitement avec Pattern Matching
&lt;/h2&gt;

&lt;p&gt;Maintenant que nous avons un arbre hiérarchique satisfaisant, penchons-nous sur le traitement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avant : Logique Procédurale Complexe
&lt;/h3&gt;

&lt;p&gt;Pour rappel :&lt;/p&gt;

&lt;p&gt;Details&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrettyPrinter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;advice&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PlayingCard&lt;/span&gt; &lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playingCard&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;TrumpCard&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;TrumpCard&lt;/span&gt; &lt;span class="n"&gt;trump&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TrumpCard&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The trump n°%s is strong"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trump&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playingCard&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;SuitCard&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;SuitCard&lt;/span&gt; &lt;span class="n"&gt;suit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SuitCard&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;face&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Logique pour cartes numériques&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The first of %s(%s) is very weak"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;suit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                        &lt;span class="n"&gt;suit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
                &lt;span class="c1"&gt;// ... autres conditions&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Logique pour cartes royales&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The %s of %s(%s) is strong"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;suit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;face&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;suit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;suit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Pattern Matching - Step by step
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Better &lt;code&gt;instanceof&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Depuis Java 16, on a le droit de simplifier nos &lt;code&gt;if’s&lt;/code&gt; à base de &lt;code&gt;instanceof&lt;/code&gt;. On peut ainsi passer de ça :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;before.java&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playingCard&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;TrumpCard&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;TrumpCard&lt;/span&gt; &lt;span class="n"&gt;trump&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TrumpCard&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The trump n°%s is strong"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trump&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;À ça :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;after.java&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playingCard&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;TrumpCard&lt;/span&gt; &lt;span class="n"&gt;trump&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The trump n°%s is strong"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trump&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;C’est pas dingue, mais au moins c’est à l’épreuve des erreurs.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;Switch expression&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Depuis Java 14, le &lt;code&gt;switch&lt;/code&gt; est une expression à par entière, qui peut donc retourner une valeur directement.&lt;/p&gt;

&lt;p&gt;On passe de ça :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;before.java&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The first of "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"("&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;") is very weak"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The second of "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"("&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;") is very weak"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// More logic&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalStateException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unexpected value: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;À ça :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;after.java&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"The first of "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"("&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;") is very weak"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"The second of "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"("&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;") is very weak"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// More logic&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalStateException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unexpected value: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;Switch&lt;/code&gt; sur le type
&lt;/h4&gt;

&lt;p&gt;Depuis Java 20, on peut &lt;em&gt;switcher&lt;/em&gt; sur les types ce qui va encore nous permettre d’améliorer les choses.&lt;/p&gt;

&lt;p&gt;On passe de ça :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;before.java&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playingCard&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;TrumpCard&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Do something&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playingCard&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;RoyalSuitCard&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
    &lt;span class="c1"&gt;// Do something&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playingCard&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;NumberSuitCard&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
    &lt;span class="c1"&gt;// Do something&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//Panick&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;À ça :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;after.java&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;TrumpCard&lt;/span&gt; &lt;span class="n"&gt;trump&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// Do something&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;NumberSuitCard&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// Do something&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;RoyalSuitCard&lt;/span&gt; &lt;span class="n"&gt;royalCard&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// Do something&lt;/span&gt;
&lt;span class="o"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ce qui est tout à fait merveilleux ici, c’est que grâce à notre hiérarchie d’objet finie, il n’y a plus besoin de paniquer. C’est le compilateur qui vérifie la complétude de notre &lt;code&gt;switch&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Destructuration des records
&lt;/h4&gt;

&lt;p&gt;Disponible dans les boucles &lt;code&gt;for&lt;/code&gt;, et dans les cas de &lt;em&gt;pattern matching&lt;/em&gt;, la destructuration nous permet d’accéder directement à ce qui nous intéresse.&lt;/p&gt;

&lt;p&gt;On passe de ça :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;before.java&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;RoyalSuitCard&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"The %s of %s(%s) is strong"&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;face&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;À ça :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;after.java&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;RoyalSuitCard&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Face&lt;/span&gt; &lt;span class="n"&gt;face&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"The %s of %s(%s) is strong"&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;face&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et on pourrait également n’exposer qu’une partie des attributs.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;Switch&lt;/code&gt; avec des &lt;em&gt;guards&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;Promis, après, j’arrête.&lt;/p&gt;

&lt;p&gt;On peut maintenant combiner tout ce que l’on a déjà avec le dernier apport : les gardes. Ils nous permettent d’ajouter des conditions plus fines dans nos &lt;em&gt;case&lt;/em&gt; :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;after.java&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playingCard&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nf"&gt;TrumpCard&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"The trump n°%s is strong"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;NumberSuitCard&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"The first of %s(%s) is very weak"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;NumberSuitCard&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"The second of %s(%s) is very weak"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;NumberSuitCard&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"The third of %s(%s) is very weak"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nc"&gt;NumberSuitCard&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt; &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"The %sth of %s(%s) is still weak"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;&lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;suitCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nf"&gt;NumberSuitCard&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"The %sth of %s(%s) may win you a hand"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nf"&gt;RoyalSuitCard&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Color&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Face&lt;/span&gt; &lt;span class="n"&gt;face&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"The %s of %s(%s) is strong"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;formatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;face&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSymbol&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ici on combine :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;un &lt;em&gt;switch expression&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;sur le type&lt;/li&gt;
&lt;li&gt;avec du pattern matching&lt;/li&gt;
&lt;li&gt;de la deconstruction&lt;/li&gt;
&lt;li&gt;et des &lt;em&gt;guards&lt;/em&gt; matérialisé par le mot clef &lt;code&gt;when&lt;/code&gt; qui nous permettent un contrôle plus fin.&lt;/li&gt;
&lt;/ul&gt;

&lt;dl&gt;
&lt;dt&gt;&lt;strong&gt;🔥 CAUTION&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;

L’ordre de vos case compte toujours autant qu’avant. On va du plus spécifique au plus générique, sinon, c’est le drame.
&lt;/dd&gt;
&lt;/dl&gt;

&lt;h2&gt;
  
  
  Bénéfices de la transformation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Réduction du Code
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Moins de boilerplate&lt;/em&gt;&lt;/strong&gt; : Les records éliminent getters, equals, hashCode, toString&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Validation centralisée&lt;/em&gt;&lt;/strong&gt; : Constructeurs compacts pour la validation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Hiérarchie simplifiée&lt;/em&gt;&lt;/strong&gt; : Élimination des classes intermédiaires&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Amélioration de la Sécurité
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Exhaustivité&lt;/em&gt;&lt;/strong&gt; : Le compilateur garantit la couverture de tous les cas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Immutabilité&lt;/em&gt;&lt;/strong&gt; : Données finales par défaut&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Typage fort&lt;/em&gt;&lt;/strong&gt; : Séparation claire des responsabilités par type&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lisibilité et Maintenabilité
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Intentions révélées&lt;/em&gt;&lt;/strong&gt; : Le code exprime clairement l’intention&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Pattern matching&lt;/em&gt;&lt;/strong&gt; : Logique métier plus lisible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Séparation des préoccupations&lt;/em&gt;&lt;/strong&gt; : Chaque type gère sa propre logique&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;L’analyse des statistiques Git révèle l’impact de cette transformation :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Réduction nette de 212 lignes&lt;/em&gt;&lt;/strong&gt; (60% de réduction), et ça c’est bon ! \o/&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="conclusion"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Comme d’habitude, ne réécrivez pas toutes vos applications tête baissée. Mais il est important de savoir quelle palette de possibilité vous offre votre langage.&lt;/p&gt;

&lt;p&gt;Gardez en tête toutes les cartes de votre main (je l’accorde, elle était facile), et restez curieux !&lt;/p&gt;

&lt;p&gt;&lt;a id="ressources"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ressources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/jtama/crazy-data-oriented-programming" rel="noopener noreferrer"&gt;Repository GitHub&lt;/a&gt; Dans la branche &lt;code&gt;main&lt;/code&gt; le code initial, dans la branche &lt;code&gt;expected&lt;/code&gt; le code final, avec un guide pour l’animer comme un kata&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/java/javase/24/language/pattern-matching.html" rel="noopener noreferrer"&gt;Documentation officielle sur les concepts de l’article&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spécial kassdédi à &lt;a href="https://www.linkedin.com/in/lucile-thienot/" rel="noopener noreferrer"&gt;Lucile Thiénot&lt;/a&gt; et &lt;a href="https://www.linkedin.com/in/floriangomas/" rel="noopener noreferrer"&gt;Florian Gomas&lt;/a&gt; ❤️!&lt;/p&gt;

</description>
      <category>java</category>
      <category>dataorientedprogramming</category>
      <category>sealedinterfaces</category>
      <category>patternmatching</category>
    </item>
    <item>
      <title>ROQ: Creating a custom section helper</title>
      <dc:creator>Kosmik</dc:creator>
      <pubDate>Tue, 01 Jul 2025 14:01:21 +0000</pubDate>
      <link>https://forem.com/onepoint/roq-creating-a-custom-section-helper-2i3c</link>
      <guid>https://forem.com/onepoint/roq-creating-a-custom-section-helper-2i3c</guid>
      <description>&lt;h2&gt;
  
  
  But why?
&lt;/h2&gt;

&lt;p&gt;Why a car? Because it’s a custom one. &lt;/p&gt;

&lt;p&gt;Now that we have answered this crucial question, let’s push it aside and move on.&lt;/p&gt;

&lt;h2&gt;
  
  
  No I meant why a custom section helper?
&lt;/h2&gt;

&lt;p&gt;Ohhhhh. Ok.&lt;/p&gt;

&lt;p&gt;Sometimes you need to execute code to generate content, not just compose templating bits.&lt;/p&gt;

&lt;p&gt;To do this &lt;em&gt;Qute&lt;/em&gt; lets you define custom section helper that allows java code execution, to transform content. Here are some existing section helpers :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mardown/Asciidoc&lt;/strong&gt;\
They will generate HTML content from doc-as-code content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QRCode&lt;/strong&gt;\
That will generate an image based on a given &lt;em&gt;URL&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What will we do?
&lt;/h2&gt;

&lt;p&gt;In this blog post, we will implement a custom section helper that allows to invoke a &lt;a href="https://kroki.io" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;em&gt;Kroki.io&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt; server to generate images from almost every possible diagram as code language.&lt;/p&gt;

&lt;p&gt;What I’d like to have is a simple and common way for the user to let us know that he is writing a diagram and would like us to generate the rendering for him.&lt;/p&gt;

&lt;p&gt;Something like :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;----
title: A blog post with a diagram
tags: qute,diagram
----

Here's my blog post:

++++ ①
\{#diagram language="dbml" outputFormat="svg" alt="Blog tables diagram" width=400 height=500}
@startuml
!theme hacker

left to right direction

object users {
id : integer
username : varchar
role : varchar
created_at : timestamp
}

object posts {
id : integer [primary key]
title : varchar
body : text
user_id : integer
status : post_status
created_at : timestamp
}

note left: **body** =&amp;gt; Content of the post

enum post_status {
draft
published
private
}

note right of post_status: **private** =&amp;gt; visible via URL only

posts::user_id --&amp;gt; users::id
posts::post_status --&amp;gt; post_status

@enduml
\{/}

++++
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Just to tell the asciidoc engine it’s a passthrough&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And I would like to be automatically converted to :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9xeicr2nl1u4lhr2l3ie.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9xeicr2nl1u4lhr2l3ie.png" alt="The resulting diagram image" width="800" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's implement this
&lt;/h2&gt;

&lt;p&gt;If you try the above code right now, Roq will bark that no section helper is available to process this section.&lt;/p&gt;

&lt;p&gt;Let’s fix this. We will just create a new &lt;code&gt;SectionHelper&lt;/code&gt; and its corresponding &lt;code&gt;factory&lt;/code&gt;, usually declared in a single source file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.quarkus.qute.EngineConfiguration&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.quarkus.qute.ResultNode&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.quarkus.qute.SectionHelper&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.quarkus.qute.SectionHelperFactory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.List&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.concurrent.CompletionStage&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@EngineConfiguration&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DiagramsSectionHelperFactory&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;SectionHelperFactory&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DiagramsSectionHelperFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DiagramSectionHelper&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getDefaultAliases&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"diagrams"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="err"&gt;②&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;DiagramSectionHelper&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SectionInitContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt;③&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DiagramSectionHelper&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DiagramSectionHelper&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;SectionHelper&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;@Override&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;CompletionStage&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ResultNode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SectionResolutionContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="err"&gt;④&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;This annotation tells the &lt;em&gt;&lt;strong&gt;&lt;em&gt;Qute&lt;/em&gt;&lt;/strong&gt;&lt;/em&gt; engine that this instance is here for him. Namely that he must register a new section helper.&lt;/li&gt;
&lt;li&gt;Here is our new section alias&lt;/li&gt;
&lt;li&gt;Where we can initialize an helper for this specific resolution context&lt;/li&gt;
&lt;li&gt;Returns exactly what was given in.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the moment, that’s not much better, the content of the section is rendered untouched, but at least, it doesn’t bark at us anymore.&lt;/p&gt;

&lt;p&gt;I will spare you the &lt;strong&gt;&lt;em&gt;Kroki.io&lt;/em&gt;&lt;/strong&gt; client implementation details, but let’s just say we have the following interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;DiagramConverter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;diagramSource&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DiagramParams&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;

    &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;DiagramParams&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;diagramLanguage&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DiagramConverter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DiagramOutputFormat&lt;/span&gt; &lt;span class="n"&gt;diagramOutputFormat&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Generates the needed &lt;code&gt;HTML&lt;/code&gt; code to display the diagram using &lt;a href="https://en.wikipedia.org/wiki/Data_URI_scheme" rel="noopener noreferrer"&gt;the data uri scheme&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Let's dive a bit more
&lt;/h3&gt;

&lt;p&gt;They are a few more methods available in the &lt;code&gt;DiagramsSectionHelperFactory&lt;/code&gt;. Let’s take a closer look at the &lt;code&gt;initialize&lt;/code&gt; one.&lt;br&gt;
For the sake of brevity, the code pretends there is only the single &lt;em&gt;language&lt;/em&gt; parameter to deal with  :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DiagramsSectionHelperFactory&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;SectionHelperFactory&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DiagramsSectionHelperFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DiagramSectionHelper&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;DiagramConverter&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;//more code here&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;DiagramSectionHelper&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SectionInitContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;diagramLanguage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"language"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DiagramSectionHelper&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;//and more here&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Gets the language param value. Be aware that it will always be a &lt;code&gt;String&lt;/code&gt;, so you may have to do conversions if needed&lt;/li&gt;
&lt;/ol&gt;

&lt;dl&gt;
&lt;dt&gt;&lt;strong&gt;❗ IMPORTANT&lt;/strong&gt;&lt;/dt&gt;
&lt;dd&gt;

Should you need to use expressions instead of literals as value, you would need a more convoluted path. That would not bite you though. Let me know if you are interested.
&lt;/dd&gt;
&lt;/dl&gt;

&lt;h2&gt;
  
  
  That's it we are done!
&lt;/h2&gt;

&lt;p&gt;Adding a new &lt;code&gt;SectionHelper&lt;/code&gt; is not hard. Of course this sample is naive, and the complexity hidden in the converter. But I am sure you can foresee useful use cases for them. Everything I've just shown is available with &lt;a href="https://quarkus.io/" rel="noopener noreferrer"&gt;&lt;strong&gt;Quarkus&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://quarkus.io/guides/qute" rel="noopener noreferrer"&gt;&lt;strong&gt;Qute&lt;/strong&gt;&lt;/a&gt; only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to dig a bit more ?
&lt;/h2&gt;

&lt;p&gt;You can first check &lt;a href="https://iamroq.com/" rel="noopener noreferrer"&gt;I am Roq&lt;/a&gt; which is already full of insights or even take a look at the &lt;a href="https://github.com/quarkiverse/quarkus-roq" rel="noopener noreferrer"&gt;QRCode plugin&lt;/a&gt; in the github repository&lt;/p&gt;

</description>
      <category>roq</category>
      <category>ascode</category>
      <category>qute</category>
      <category>howtos</category>
    </item>
    <item>
      <title>Even if you don’t like Markdown, sometimes you don’t have a choice</title>
      <dc:creator>Kosmik</dc:creator>
      <pubDate>Wed, 04 Jun 2025 15:07:05 +0000</pubDate>
      <link>https://forem.com/onepoint/even-if-you-dont-like-markdown-sometimes-you-dont-have-a-choice-4436</link>
      <guid>https://forem.com/onepoint/even-if-you-dont-like-markdown-sometimes-you-dont-have-a-choice-4436</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 🔥&lt;/span&gt;
downdoc &lt;span class="nt"&gt;-o&lt;/span&gt; out.md README.adoc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💀 &lt;em&gt;Opinions are my own. I may have to exagerate them for obvious narrative reasons.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Yes, you hate &lt;strong&gt;Markdown&lt;/strong&gt;, and yet it seems to spread like a disease.&lt;/p&gt;

&lt;p&gt;Yet, textual format documentation is an absolute necessity for us developers, and I am sure you already have found your challenger. I am talking, of course, about &lt;strong&gt;ASCIIDOC&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Guess you would not be here if you hadn’t.&lt;/p&gt;

&lt;p&gt;But there are some cases where you just don’t have a choice and have to produce &lt;strong&gt;Markdown&lt;/strong&gt;. 😢&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4xtw6ungngrrhpiyak2q.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4xtw6ungngrrhpiyak2q.jpg" alt="Saaad keanu crying about markdown flavours" width="601" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  You are not doomed.
&lt;/h2&gt;

&lt;p&gt;It seems that &lt;a href="https://github.com/mojavelinux" rel="noopener noreferrer"&gt;Dan Allen&lt;/a&gt;, father of &lt;a href="https://asciidoctor.org/" rel="noopener noreferrer"&gt;Asciidoctor&lt;/a&gt;, agrees with you 100%.&lt;/p&gt;

&lt;p&gt;Strange, isn’t it?&lt;/p&gt;

&lt;p&gt;Anyway, he also fathered &lt;a href="https://github.com/opendevise/downdoc" rel="noopener noreferrer"&gt;Downdoc&lt;/a&gt;... That name is absolutely marvelous, and here is its headline:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rapidly down-converts AsciiDoc to Markdown.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can either use &lt;em&gt;npx&lt;/em&gt; to use it :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx downdoc &lt;span class="nt"&gt;-o&lt;/span&gt; out.md README.adoc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or install it using &lt;a href="https://mise.jdx.dev/" rel="noopener noreferrer"&gt;mise&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mise use npm:downdoc@latest
downdoc &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  All hail to &lt;em&gt;ASCIIDOC&lt;/em&gt; !
&lt;/h3&gt;

&lt;p&gt;Cover image by &lt;a href="https://www.pexels.com/photo/crop-person-squeezing-orange-in-bowl-5946786/" rel="noopener noreferrer"&gt;Charlotte May&lt;/a&gt;&lt;/p&gt;

</description>
      <category>markdown</category>
      <category>asciidoc</category>
      <category>howtos</category>
    </item>
    <item>
      <title>Changing the menu from your Roq powered blog</title>
      <dc:creator>Kosmik</dc:creator>
      <pubDate>Wed, 21 May 2025 14:43:16 +0000</pubDate>
      <link>https://forem.com/onepoint/changing-the-menu-from-your-roq-powered-blog-5ac9</link>
      <guid>https://forem.com/onepoint/changing-the-menu-from-your-roq-powered-blog-5ac9</guid>
      <description>&lt;p&gt;Roq, as many other static site generators, uses a template engine to generate the final HTML. The template engine used by Roq is &lt;a href="https://quarkus.io/guides/qute" rel="noopener noreferrer"&gt;Qute&lt;/a&gt;. It’s a powerful engine that -amongst other things- leverages template inheritance.&lt;/p&gt;

&lt;p&gt;There is a small introduction on &lt;a href="https://iamroq.com/docs/advanced/#_themes" rel="noopener noreferrer"&gt;iamroc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I read it, I re-read it, I thought I had understood. I hadn’t understood. So you’re like me, and like to understand how things are build, follow me.&lt;/p&gt;

&lt;p&gt;If you just want to change the menu layout, you can skip the first two chapters&lt;/p&gt;

&lt;h2&gt;
  
  
  Hypothesis
&lt;/h2&gt;

&lt;p&gt;I will make the following assumptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have a Roq powered blog. That seems obvious, but at least, now we know.&lt;/li&gt;
&lt;li&gt;Tour blog uses the default theme: The one and only &lt;a href="https://github.com/quarkiverse/quarkus-roq/tree/main/roq-theme" rel="noopener noreferrer"&gt;roq-default theme&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because by default any blog post is part of the &lt;a href="https://github.com/quarkiverse/quarkus-roq/blob/3cc5af93600cd3fb68fcbb2d28b57527eb12e11d/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/config/RoqSiteConfig.java#L25" rel="noopener noreferrer"&gt;&lt;code&gt;posts&lt;/code&gt; collection&lt;/a&gt;, it’s setup to use the &lt;code&gt;:theme/post&lt;/code&gt; template.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we want to achieve
&lt;/h2&gt;

&lt;p&gt;Whate we’re going to do in this blog post is to move the menu from the left side of the page to the top of the page.&lt;br&gt;
As below :&lt;br&gt;
From this&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxv7ps3tgz9kr48hmiwji.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxv7ps3tgz9kr48hmiwji.png" alt="Before" width="238" height="225"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;To this&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fna3o8lzoarzwvo40gd9l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fna3o8lzoarzwvo40gd9l.png" alt="After" width="619" height="104"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Inheritance
&lt;/h2&gt;

&lt;p&gt;A template can have a parent but can also have multiple children may they be partials/fragment.&lt;/p&gt;

&lt;p&gt;To be able to determine what &lt;code&gt;HTML&lt;/code&gt; is expected to be generated for a given page, the Qute engine will look up its layout inheritance tree, combine it with the page content and generate the final HTML.&lt;/p&gt;

&lt;p&gt;The default inheritance tree layout for your blog post pages is as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8fivk186siwxrskm0ik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8fivk186siwxrskm0ik.png" alt="The default inheritance tree" width="727" height="664"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So that you don’t have do it yourself, I’ve searched where the main page structure is actually contributed. It’s in the &lt;code&gt;theme-layouts/roq-default/main.html&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;---
layout: roq-default/default
---

{@io.quarkiverse.roq.frontmatter.runtime.model.Site site}

&lt;span class="nt"&gt;&amp;lt;aside&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sidebar main"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {#insert header}
  &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
    {#insert about}{#include partials/roq-default/sidebar-about /}{/}
    {#insert menu} ①
    {#include partials/roq-default/sidebar-menu menu=cdi:menu.items /} ②
    {/}
  &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- End Header --&amp;gt;&lt;/span&gt;
  {/}
  {#insert footer}
  &lt;span class="nt"&gt;&amp;lt;footer&amp;gt;&lt;/span&gt;
    {#insert contact}{#include partials/roq-default/sidebar-contact /}{/}
    {#insert copyright}{#include partials/roq-default/sidebar-copyright /}{/}
  &lt;span class="nt"&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- End Footer --&amp;gt;&lt;/span&gt;
  {/}
&lt;span class="nt"&gt;&amp;lt;/aside&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- End Sidebar --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content-box clearfix"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {#insert /} ③
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;The {#insert &amp;lt;name&amp;gt;} syntax allows to define a new named section. The &lt;code&gt;menu&lt;/code&gt; section, in our case.&lt;/li&gt;
&lt;li&gt;In this case, the templates also provides a default value for the section, that we will have to override.&lt;/li&gt;
&lt;li&gt;The {#insert /} syntax is used to include the content of the current page inheriting the layout.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;&lt;em&gt;What if&lt;/em&gt;&lt;/em&gt; I could override the &lt;code&gt;menu&lt;/code&gt; section?
&lt;/h3&gt;

&lt;p&gt;Yes of course this is possible and even the right way to go.&lt;/p&gt;

&lt;p&gt;We can create a template if our project called &lt;code&gt;Main.html&lt;/code&gt; and save it to the &lt;code&gt;templates/layouts/rod-default&lt;/code&gt; folder. The template will inherit from the &lt;code&gt;theme-layouts/roq-default/main.html&lt;/code&gt; template, and override the &lt;code&gt;menu&lt;/code&gt; section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;---
layout: theme-layouts/roq-default/main
---

{#menu} {/} ①

{#include partials/roq-default/sidebar-menu menu=cdi:menu.items /} ②

{#insert /} ③
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Redifine an empty &lt;code&gt;menu&lt;/code&gt; section (to remove it from the side bar)&lt;/li&gt;
&lt;li&gt;Reusing the same partials but injecting elsewhere.&lt;/li&gt;
&lt;li&gt;Insert the content of the current page inheriting the layout.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The dependency tree as now a small injection:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3o7vgceidm31xfcfawu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3o7vgceidm31xfcfawu.png" alt="The amended inheritance tree" width="800" height="803"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And all I need now is little bit of css (in your &lt;code&gt;src\main\resources\web.app&lt;/code&gt; folder), and we are off for a good start :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.menu&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;a&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;list-style-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;grid-auto-rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;min-content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;grid-auto-flow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Inheritance is not hard to understand, but you should have your theme code open to be sure where you can hook and how.&lt;/p&gt;

&lt;p&gt;If you want more in depth documentation, you can check the 👉 &lt;a href="https://quarkus.io/guides/qute" rel="noopener noreferrer"&gt;Qute documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’m sure you have a whole lot of ideas, and I’d be glad to see your roq site, so don’t hesitate to share in the comments.&lt;/p&gt;

</description>
      <category>roq</category>
      <category>qute</category>
      <category>theme</category>
      <category>ascode</category>
    </item>
    <item>
      <title>Adding mermaid to your Roq powered blog</title>
      <dc:creator>Kosmik</dc:creator>
      <pubDate>Wed, 14 May 2025 12:59:39 +0000</pubDate>
      <link>https://forem.com/onepoint/adding-mermaid-to-your-roq-powered-blog-45ic</link>
      <guid>https://forem.com/onepoint/adding-mermaid-to-your-roq-powered-blog-45ic</guid>
      <description>&lt;h2&gt;
  
  
  Having wunderbar diagrams in your blog
&lt;/h2&gt;

&lt;p&gt;&lt;a id="Diagram"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3aswz3c7487rxb2pc38q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3aswz3c7487rxb2pc38q.png" alt="Image description" width="452" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So you have a Roq powered blog, and you want to add Mermaid to it. More generally, you want to add content to your blog that is not handled by Roq, but rather uses a third party js library.&lt;/p&gt;

&lt;p&gt;I will focus on adding Mermaid, but the same approach can be used for any other library.&lt;/p&gt;

&lt;p&gt;Simply use your imagination.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️&lt;br&gt;
Roq philosophy is to generate as much as possible at build time and to do as little as possible at runtime. Alas, this is not always possible or suitable for you need. So once you have considered AND excluded build time generation, here is a workaround.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To add js lib to your Roq blog, you have two ways.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding the third party library dependency using &lt;a href="https://mvnpm.org" rel="noopener noreferrer"&gt;mvnpm&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;If the third party library you're looking for is available on &lt;a href="https://mvnpm.org" rel="noopener noreferrer"&gt;mvnpm&lt;/a&gt;, you can simply add it to your &lt;code&gt;pom.xml&lt;/code&gt; file as a dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.mvnpm&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;mermaid&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;11.6.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;provided&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;exclusions&amp;gt;&lt;/span&gt; 1️⃣
        &lt;span class="nt"&gt;&amp;lt;exclusion&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;*&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;*&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/exclusion&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/exclusions&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;1️⃣ We don't want any transitive dependencies, you might not have to add exlusions for other libraries&lt;/p&gt;

&lt;h2&gt;
  
  
  Bootstrapping the lib
&lt;/h2&gt;

&lt;p&gt;We need to import the lib and bootstrap it in our project, so that the web bundler can do its black magic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mermaid&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mermaid/dist/mermaid.esm.min.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;mermaid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;startOnLoad&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding a custom tag to our blog
&lt;/h2&gt;

&lt;p&gt;To prevent the blog's writer from having to repeat the needed steps to generate the diagram, we will create a custom tag.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;&amp;lt;root&amp;gt;/template/tags&lt;/code&gt; folder, create a new file called &lt;code&gt;mermaid.html&lt;/code&gt; and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;pre&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mermaid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {nested-content}
&lt;span class="nt"&gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the newly created tag
&lt;/h2&gt;

&lt;p&gt;In your blog post you can now use the new tag like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{#mermaid}
{|
---
config:
layout: elk
look: handDrawn
theme: dark
---
flowchart TB
A[Start] --&amp;gt; B{"Let's try Roq"}
B --&amp;gt;|Yes| C[I am Roq]
B --&amp;gt;|No| D["Ohh noo :("]
|}
{/}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You already saw the rendered result at the beginning of this post, but I grant you it was marvelous, so let's look at it again.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about the other way ?
&lt;/h2&gt;

&lt;p&gt;You can always skip the first two steps and amend your custom tag as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;pre&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mermaid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    {nested-content}
&lt;span class="nt"&gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mermaid&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;mermaid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;startOnLoad&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hope you've learned something new, and maybe given you ideas ;)&lt;/p&gt;

&lt;p&gt;More guides &lt;a href="https://iamroq.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt; and you can always follow/give a star on &lt;a href="https://github.com/quarkiverse/quarkus-roq" rel="noopener noreferrer"&gt;the github repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>roq</category>
      <category>swagger</category>
      <category>qute</category>
    </item>
    <item>
      <title>So you wanna know where your maven dependency version comes from ?</title>
      <dc:creator>Kosmik</dc:creator>
      <pubDate>Wed, 26 Mar 2025 09:29:32 +0000</pubDate>
      <link>https://forem.com/onepoint/so-you-wanna-know-where-your-maven-dependency-version-comes-from--244d</link>
      <guid>https://forem.com/onepoint/so-you-wanna-know-where-your-maven-dependency-version-comes-from--244d</guid>
      <description>&lt;p&gt;You work with a complex maven project. That's a fact, otherwise you wouldn't be here, searching to uncover a way out of its dependencies maze.&lt;/p&gt;

&lt;p&gt;Your project probably has a &lt;code&gt;parent&lt;/code&gt;, as your &lt;code&gt;parent&lt;/code&gt; might also have one.&lt;/p&gt;

&lt;p&gt;You may have &lt;code&gt;boms&lt;/code&gt; in your dependency management, but your &lt;code&gt;parent&lt;/code&gt; certainly has one too.&lt;/p&gt;

&lt;p&gt;You defined &lt;code&gt;properties&lt;/code&gt;, and guess what, so have at least a dozen direct or transitive dependencies.&lt;/p&gt;

&lt;p&gt;So after having done your research, not relying on some gibberish from whatever AI assistant you usually use, you found out about :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn dependency:tree
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While that does give you a nice console output, chances are it's not enough. Again, you wouldn't be here if it was.&lt;/p&gt;

&lt;p&gt;It does tell you the dependency version used, where they were pulled out from, but why is that specific version used can still be unclear.&lt;/p&gt;

&lt;p&gt;Let's say you have a dependency somewhere in you project hierarchy defined as followed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-web&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${spring.boot.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The property value is defined to be &lt;code&gt;3.4.4&lt;/code&gt; in your parent, but the the &lt;code&gt;dependency:tree&lt;/code&gt; plugin output tells you the value is &lt;code&gt;2.7.9&lt;/code&gt;, and so you rightfully ask yourself, why the f... is that.&lt;/p&gt;

&lt;p&gt;Well it just happens that this can be easily solved using the &lt;code&gt;help&lt;/code&gt; plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn &lt;span class="nb"&gt;help&lt;/span&gt;:effective-pom &lt;span class="nt"&gt;-Dverbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will spit out your pom as seen by the great &lt;code&gt;maven&lt;/code&gt; itself. The only issue is that chances are great that your console buffer will be  overflowed. But we are smarter than that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn &lt;span class="nb"&gt;help&lt;/span&gt;:effective-pom &lt;span class="nt"&gt;-Dverbose&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;spring.boot.version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will give you something like :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &amp;lt;spring.boot.version&amp;gt;2.7.9&amp;lt;/spring.boot.version&amp;gt;  &lt;span class="se"&gt;\&lt;/span&gt;
    &amp;lt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;--&lt;/span&gt; com.acme:your.exotic.dependency:17.0.18-RC3-FINAL-GA, line 253 &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There! You have it! Of course your exotic dependency is the culprit. Now that you know it, it will be much easier to fix. Don't you think ?&lt;/p&gt;

</description>
      <category>java</category>
      <category>maven</category>
      <category>dependencies</category>
    </item>
    <item>
      <title>To `Gather` or not to `Gather`? That is the question.</title>
      <dc:creator>Kosmik</dc:creator>
      <pubDate>Mon, 24 Mar 2025 08:49:34 +0000</pubDate>
      <link>https://forem.com/onepoint/to-gather-or-not-to-gather-that-is-the-question-36oo</link>
      <guid>https://forem.com/onepoint/to-gather-or-not-to-gather-that-is-the-question-36oo</guid>
      <description>&lt;h2&gt;
  
  
  Mais de quoi va-t-on parler ?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;De Java&lt;/strong&gt;. Oui je préfère le dire dès le début. Cet article va parler de &lt;strong&gt;Java&lt;/strong&gt; !&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh9r8tyytssozkywyup5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh9r8tyytssozkywyup5f.png" alt="logo java" width="600" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Les &lt;code&gt;gatherers&lt;/code&gt; sont le premier ajout d'&lt;strong&gt;importance&lt;/strong&gt; à l’API &lt;code&gt;java.util.Streams&lt;/code&gt; depuis sa sortie, et on parle de &lt;strong&gt;2014&lt;/strong&gt;. Cela signifie qu’il ne s’était pas passé grand-chose depuis &lt;strong&gt;environ longtemps&lt;/strong&gt;. &lt;br&gt;
Arrivés en &lt;em&gt;preview&lt;/em&gt; en Java 22, il sont finalement standards en Java 24.&lt;/p&gt;
&lt;h2&gt;
  
  
  Table des matières
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Remise à niveau&lt;/li&gt;
&lt;li&gt;Rappels&lt;/li&gt;
&lt;li&gt;Collectors vs Gatherers&lt;/li&gt;
&lt;li&gt;L'interface &lt;code&gt;Gatherer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
Let's code!

&lt;ul&gt;
&lt;li&gt;On recode la méthode &lt;code&gt;filter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Un &lt;code&gt;Stream&lt;/code&gt; avec un index !?&lt;/li&gt;
&lt;li&gt;Un &lt;code&gt;groupingBy&lt;/code&gt;, mais pas terminal.&lt;/li&gt;
&lt;li&gt;Et maintenant, on fusionne des &lt;code&gt;Streams&lt;/code&gt;!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="Quiz"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Remise à niveau
&lt;/h2&gt;

&lt;p&gt;Même si on ne va pas faire une revue complète de l'&lt;em&gt;API&lt;/em&gt; &lt;code&gt;java.util.Streams&lt;/code&gt;, je vous propose de commencer par un petit quizz, &lt;em&gt;tranquilles, posés&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rovt2374sjtw4bj1nee.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7rovt2374sjtw4bj1nee.png" alt="Shakespeare posant une question" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Quiz 1/3
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Quizz&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;        &lt;span class="n"&gt;getPeople&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Admettons que la méthode &lt;code&gt;getPeople&lt;/code&gt; renvoie une instance de &lt;code&gt;Stream&lt;/code&gt; de 10 objets de type &lt;code&gt;Person&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ici, le code est simple, on applique la méthode &lt;code&gt;map&lt;/code&gt; qui prend en paramètre une &lt;code&gt;Function&lt;/code&gt;, qui extraie une &lt;code&gt;String&lt;/code&gt; à partir d'une instance de &lt;code&gt;Person&lt;/code&gt; en renvoyant le résultat de la méthode &lt;code&gt;Person.getName()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;La question est : &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Combien de fois la ligne 4 est-elle invoquée ?&lt;/p&gt;

&lt;p&gt;① 1 fois ?&lt;br&gt;
② 10 fois ?&lt;br&gt;
③ Non Jérôme, elle n'est pas invoquée la ligne 4.&lt;br&gt;
④ &lt;em&gt;It depends!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  spoiler
  &lt;br&gt;
Vous êtes-vous dit qu’il y avait plusieurs bonnes réponses ? 

&lt;p&gt;La bonne réponse est: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;④ &lt;em&gt;It depends!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;En réalité, la question était mal posée. La question, aurait du être : &lt;/p&gt;

&lt;p&gt;"Est-ce que la méthode &lt;code&gt;map&lt;/code&gt; est invoquée une fois, 10 fois, ainsi  de suite ?" ou "Est-ce que la fonction qui est passée en paramètre de la méthode &lt;code&gt;map&lt;/code&gt; est invoquée ?"&lt;/p&gt;

&lt;p&gt;La méthode &lt;code&gt;map&lt;/code&gt;, est bien appelée une seule fois, par contre, la fonction qui lui est passée en paramètre, elle, est bien invoquée 10 fois. &lt;/p&gt;

&lt;p&gt;Il faut systématiquement différencier les méthodes de l’API Stream, qui ne font que de la configuration de pipeline, et les fonctions/predicats/... qu’on leur passe.&lt;/p&gt;



&lt;/p&gt;

&lt;h3&gt;
  
  
  Quiz 2/3
&lt;/h3&gt;

&lt;p&gt;Bon, ok, on a compris. À partir de maintenant, la question portera systématiquement sur le nombre d’invocations de la &lt;code&gt;Function&lt;/code&gt; passée en paramètre de la méthode &lt;code&gt;map&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Quizz&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;        &lt;span class="n"&gt;getPeople&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;La question est : &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Combien de fois la ligne 4 est-elle invoquée ?&lt;br&gt;
① 1 fois ?&lt;br&gt;
② 10 fois ?&lt;br&gt;
③ Non Jérôme, elle n'est pas invoquée la ligne 4.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  spoiler
  &lt;p&gt;La bonne réponse est : &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;③ &lt;em&gt;Non Jérôme, elle n’est pas invoquée la ligne 4.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dans un &lt;code&gt;Stream&lt;/code&gt;, il y a deux types d’opérations : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Des opérations intermédiaires.&lt;/li&gt;
&lt;li&gt;Des opérations finales. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les opérations intermédiaires ne font que configurer un pipeline d’exécution, elles ne déclenchent rien. Tant qu’on n’a pas appelé une méthode finale sur un &lt;code&gt;stream&lt;/code&gt;, il ne se passe rien du tout.&lt;br&gt;
&lt;/p&gt;

&lt;/p&gt;
&lt;h3&gt;
  
  
  Quiz 3/3
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Quiz&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;     &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;         &lt;span class="n"&gt;getPeople&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;             &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;             &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;     &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;La question est : &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Combien de fois la ligne 4 est-elle invoquée ?&lt;br&gt;
① 1 fois ?&lt;br&gt;
② 10 fois ?&lt;br&gt;
③ Non Jérôme, elle n'est pas invoquée la ligne 4.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  spoiler
  &lt;p&gt;La bonne réponse est : &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;③ &lt;em&gt;Non Jérôme, elle n’est pas invoquée la ligne 4.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Eeeeeeet oui, l’API &lt;code&gt;stream&lt;/code&gt; est intelligente, et parmi les opérations terminales, il y en a  certaines qui possèdent des &lt;em&gt;raccourcis&lt;/em&gt;. Et c'est le cas de la méthode &lt;code&gt;count&lt;/code&gt; qui est capable d’évaluer si toutes les opérations qui ont été exécutées avant elle peuvent avoir un impact sur la cardinalité de ce qu’il y a en sortie. &lt;/p&gt;

&lt;p&gt;Ici, une seule opération &lt;code&gt;map&lt;/code&gt; n’aura aucun impact sur la cardinalité, et donc, inutile de l’invoquer, ça n’a aucun intérêt. On n’a pas besoin de transformer des personnes en chaînes de caractères pour savoir qu’il y en à 10. &lt;/p&gt;

&lt;p&gt;C'est assez important de comprendre que vous n'avez pas de garantie d'invocation. Et si, par exemple, vous faites partie de la team &lt;code&gt;peek&lt;/code&gt;-&lt;br&gt;
&lt;span&gt;💀 ARRÊTEZ-CA MAINTENANT 💀&lt;/span&gt;-, et que vous utilisez un &lt;code&gt;count&lt;/code&gt;, votre &lt;code&gt;peek&lt;/code&gt; risquerait bien de ne jamais être invoqué.&lt;br&gt;
&lt;/p&gt;

&lt;/p&gt;

&lt;p&gt;&lt;a id="Rappels"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Rappels
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Il y a des méthodes intermédiaires : &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Il y a des méthodes terminales : &lt;code&gt;anyMatch&lt;/code&gt;, &lt;code&gt;toList&lt;/code&gt;, &lt;code&gt;count&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;L’invocation d’une opération terminale est le seul déclencheur de l’utilisation d’un pipeline. Je peux enchaîner autant d’opérations intermédiaires que je veux, il ne se passera jamais rien. &lt;/p&gt;

&lt;p&gt;Et dès que j’appelle une opération terminale, je consomme mon stream, et je ne peux plus rien en faire. Et évidemment, un peu tautologique, je ne peux pas avoir deux méthodes terminales qui s’enchaînent.  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Certaines opérations terminales possèdent des courts-circuits. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On peut implémenter un nombre infini ♾️ d'opérations terminales grâce à l'API Collector.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Il existe un nombre fini d'opérations intermédiaires fournies pas l'API&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Évidemment, l’API gatherer est là pour rattraper cette terrible injustice et nous laisser la capacité de coder toutes les opérations intermédiaires que l’on veut.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="CollectorsVsGatherers"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Collectors vs Gatherers
&lt;/h2&gt;

&lt;p&gt;Nous sommes habitués à l'API &lt;code&gt;Collectors&lt;/code&gt;, mais tout de même rafraîchissons nous la mémoire.&lt;/p&gt;
&lt;h3&gt;
  
  
  Les &lt;em&gt;Collectors&lt;/em&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Collector&lt;/span&gt; &lt;span class="n"&gt;collector&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="err"&gt;①&lt;/span&gt;

&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;groupingBy&lt;/span&gt;&lt;span class="o"&gt;(...);&lt;/span&gt;&lt;span class="err"&gt;②&lt;/span&gt;

&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collector&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;&lt;span class="err"&gt;③&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;① L'interface &lt;code&gt;Collector&lt;/code&gt; que nous devons implémenter.&lt;br&gt;
② La classe &lt;code&gt;Collectors&lt;/code&gt;, qui fourni un certain nombre de &lt;em&gt;collectors&lt;/em&gt; déjà implémentés&lt;br&gt;
③ L'utilisation d'un &lt;em&gt;collector&lt;/em&gt; via la méthode &lt;code&gt;Stream#collect&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Et maintenant leurs jumeaux&lt;/p&gt;
&lt;h3&gt;
  
  
  Les &lt;em&gt;Gatherers&lt;/em&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Gatherer&lt;/span&gt; &lt;span class="n"&gt;gatherer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="err"&gt;①&lt;/span&gt;

&lt;span class="nc"&gt;Gatherers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;windowFixed&lt;/span&gt;&lt;span class="o"&gt;(...);&lt;/span&gt;&lt;span class="err"&gt;②&lt;/span&gt;

&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;gather&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gatherer&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;&lt;span class="err"&gt;③&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;① L'interface &lt;code&gt;Gatherer&lt;/code&gt; que nous devons implémenter.&lt;br&gt;
② La classe &lt;code&gt;Gatherers&lt;/code&gt;, qui fourni un certain nombre de &lt;em&gt;gatherers&lt;/em&gt; déjà implémentés. Ici &lt;code&gt;windowFixed&lt;/code&gt; qui accumule &lt;em&gt;n&lt;/em&gt; éléments avant de les pousser dans le &lt;code&gt;stream&lt;/code&gt; sous forme de liste.&lt;br&gt;
③ L'utilisation d'un &lt;em&gt;gatherer&lt;/em&gt; via la méthode &lt;code&gt;Stream#gather&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Code utilisé pour les exemples
&lt;/h2&gt;

&lt;p&gt;À partir de maintenant, tous les exemples suivront le code suivant&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;oeuvres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;prettyPrint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
       &lt;span class="n"&gt;oeuvres&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;gather&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oeuvre&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;titre&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"N"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
       &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
   &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;titre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
   &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;anneeParution&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
   &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;perdue&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;① C'est ici que nous placerons les gatherer &lt;em&gt;custom&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="Gatherer"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  L'interface &lt;code&gt;Gatherer&lt;/code&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;💡&lt;br&gt;
Le code que nous allons regarder n'est pas le vrai code, mais une version épurée de l'interface.&lt;br&gt;
Si vous voulez voir le vrai code, je rappelle que &lt;a href="https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/stream/Gatherer.java" rel="noopener noreferrer"&gt;tout ceci est libre d'accès&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;java.util.stream&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Gatherer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;A&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nc"&gt;Supplier&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;A&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;initializer&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;

    &lt;span class="nc"&gt;Integrator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;A&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;integrator&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="err"&gt;②&lt;/span&gt;

    &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nc"&gt;BinaryOperator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;A&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;combiner&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="err"&gt;③&lt;/span&gt;

    &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nc"&gt;BiConsumer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;A&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Downstream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;super&lt;/span&gt; &lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;finisher&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="err"&gt;④&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;On peut déjà constater que l'interface fait une utilisation massive des génériques.&lt;/p&gt;

&lt;p&gt;Pour les illustrer, prenons l'exemple d'un &lt;em&gt;gatherer&lt;/em&gt; qui réimplémente l'opération intermédaire &lt;code&gt;map&lt;/code&gt; (d'une instance d'&lt;code&gt;Oeuvre&lt;/code&gt; vers une &lt;code&gt;String&lt;/code&gt; en utilisant la méthode &lt;code&gt;Oeuvre#titre()&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;Le type &lt;code&gt;T&lt;/code&gt; représente le type de l'objet entrant, ici &lt;code&gt;Oeuvre&lt;/code&gt;. Le type &lt;code&gt;A&lt;/code&gt; représente le type de l'état du &lt;em&gt;gatherer&lt;/em&gt; (on y reviendra). Le type &lt;code&gt;R&lt;/code&gt; représente le type de retour du &lt;em&gt;gatherer&lt;/em&gt;, ici &lt;code&gt;String&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  ① La méthode &lt;code&gt;initializer&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Elle permet d'initialiser l'état, si besoin. Elle possède une implémentation par défaut et renvoie un &lt;code&gt;Supplier&lt;/code&gt; d'état. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️&lt;br&gt;
Attention, elle ne renvoie pas un nouvel état, mais un &lt;code&gt;Supplier&lt;/code&gt; d'état.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  ② La méthode &lt;code&gt;integrator&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Son rôle est de retourner un &lt;code&gt;Integrator&lt;/code&gt;. C'est l'objet qui va intervenir sur le stream, et sur lequel nous allons revenir tout au long des exemples.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️&lt;br&gt;
Attention, encore une fois, elle n'implémente pas la méthode qui agit. C'est une &lt;em&gt;factory&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  ③ La méthode &lt;code&gt;combiner&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Elle renvoie un &lt;code&gt;BinaryOperator&amp;lt;X&amp;gt;&lt;/code&gt;, c'est à dire, une &lt;code&gt;BiFunction&amp;lt;X,X,X&amp;gt;&lt;/code&gt;. Son rôle est de combiner les états en cas d'exécutions parallèles.&lt;/p&gt;
&lt;h3&gt;
  
  
  ④ La méthode &lt;code&gt;finisher&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Elle renvoie un &lt;code&gt;BinaryConsumer&lt;/code&gt;, qui permet en cas de besoin d'exécuter une action en fin de traitement.&lt;/p&gt;

&lt;p&gt;&lt;a id="Letscode"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's code !
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Oui, ok, t'es mignon, mais c'est quand même super abstrait ce que tu nous racontes là.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Et c'est vrai !&lt;/p&gt;

&lt;p&gt;Je vous propose donc de redévelopper la méthode &lt;code&gt;filter&lt;/code&gt; dont vous connaissez déjà le fonctionnement.&lt;/p&gt;

&lt;p&gt;&lt;a id="filter"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  On recode la méthode &lt;code&gt;filter&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;C'est un &lt;code&gt;gatherer&lt;/code&gt; simple, donc nous n'aurons besoin que d'implémenter la méthode &lt;code&gt;integrator&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Nous allons l'implémenter à base d'&lt;em&gt;anonymous inner class&lt;/em&gt;. À l'ancienne.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;org.github.jtama.gatherornot&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.function.Predicate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.stream.Gatherer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Gatherer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Predicate&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nc"&gt;Filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Predicate&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Integrator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;integrator&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Integrator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;@Override&lt;/span&gt;
            &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;integrate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="err"&gt;④&lt;/span&gt;
              &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
              &lt;span class="nc"&gt;Oeuvre&lt;/span&gt; &lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;②&lt;/span&gt;
              &lt;span class="nc"&gt;Downstream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;super&lt;/span&gt; &lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt;③&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;};&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;① L'état que nous ignorons pour l'instant.&lt;br&gt;
② L'instance d'&lt;code&gt;Oeuvre&lt;/code&gt; en cours de traitement dans le &lt;code&gt;stream&lt;/code&gt;.&lt;br&gt;
③ Le &lt;code&gt;downstream&lt;/code&gt; représente ce qui vient après dans le &lt;code&gt;stream&lt;/code&gt;.&lt;br&gt;
④ La méthode retourne un &lt;code&gt;boolean&lt;/code&gt; qui permet d'indiquer à l'API &lt;code&gt;stream&lt;/code&gt; si le gatherer accepte d'autres éléments. Comme, il s'agit d'un filtre, nous renvoyons toujours &lt;code&gt;true&lt;/code&gt; ou la propagation du résultat de la méthode &lt;code&gt;downstream.push&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ici, pas vraiment de difficulté, mais un code vraiment verbeux que l'on va pouvoir simplifier.&lt;/p&gt;

&lt;p&gt;L'implémentation de la classe &lt;code&gt;Integrator&lt;/code&gt; ne contient qu'une méthode. On peut donc écrire une lambda.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;org.github.jtama.gatherornot&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.function.Predicate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.stream.Gatherer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Gatherer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Predicate&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nc"&gt;Filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Predicate&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Integrator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;integrator&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;};&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;① La variable &lt;code&gt;state&lt;/code&gt; n'étant pas utilisée, on peux utiliser un &lt;code&gt;_&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;L'implémentation de la classe &lt;code&gt;Filter&lt;/code&gt; ne contient également qu'une méthode. Donc rebelote, transformation en lambda.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;org.github.jtama.gatherornot&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.function.Predicate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.stream.Gatherer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Gatherer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;?,&lt;/span&gt; &lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Predicate&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;};&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Et voilà. C'est plus court. C'est mieux.&lt;/p&gt;

&lt;p&gt;Non.&lt;/p&gt;

&lt;p&gt;On a beaucoup perdu en lisibilité, mais les interfaces &lt;code&gt;Gatherer&lt;/code&gt; et &lt;code&gt;Integrator&lt;/code&gt; offrent des méthodes utilitaires pour la regagner.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;org.github.jtama.gatherornot&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.function.Predicate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.stream.Gatherer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.stream.Gatherer.Integrator&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Gatherer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;?,&lt;/span&gt; &lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Predicate&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Gatherer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Integrator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofGreedy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;①&lt;/span&gt;
                    &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                        &lt;span class="o"&gt;}&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                    &lt;span class="o"&gt;}));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;① On utilise ici la méthode &lt;code&gt;ofGreedy&lt;/code&gt; (pour ceux qui ne parlent pas couramment anglais, &lt;em&gt;greedy&lt;/em&gt; veut dire &lt;em&gt;avide&lt;/em&gt;, allez tout de suite regarder le film &lt;strong&gt;Se7en&lt;/strong&gt;), qui permet de dire à l'API &lt;code&gt;stream&lt;/code&gt; que ce &lt;code&gt;Gatherer&lt;/code&gt; n'interrompra jamais de lui même la consommation du stream, et qui permet à l'API &lt;code&gt;stream&lt;/code&gt; de faire des optimisations.&lt;/p&gt;

&lt;p&gt;À partir de maintenant, nous utiliserons toujours cette façon d'écrire le code.&lt;/p&gt;

&lt;p&gt;&lt;a id="WithIndex"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Un stream avec un index ?
&lt;/h3&gt;

&lt;p&gt;N'avez-vous déjà pas eu envie d'accéder à l'index de l'élément en cours de traitement ? N'avez vous pas déjà essayé l'implémentation suivante ?&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;oeuvres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;AtomicInteger&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AtomicInteger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;&lt;span class="err"&gt;①&lt;/span&gt;
&lt;span class="n"&gt;oeuvres&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAndIncrement&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;&lt;span class="err"&gt;②&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;① On utilise un &lt;code&gt;AtomicInteger&lt;/code&gt; pour conserver/incrémenter l'index&lt;br&gt;
② En admettant que la classe &lt;code&gt;Tuple&lt;/code&gt; existe&lt;/p&gt;

&lt;p&gt;C'est une approche qui fonctionne très bien jusqu'à ce que quelqu'un ait la bonne idée d'ajouter un petit &lt;code&gt;.parallel()&lt;/code&gt; avant. &lt;/p&gt;

&lt;p&gt;Et là :&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5ds1s94acuikse0cs2f.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5ds1s94acuikse0cs2f.gif" alt="C'est le drame" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bon je me permets d'ajouter qu'une bonne vieille boucle &lt;code&gt;for&lt;/code&gt; est certainement ce qu'il vous faut.&lt;/p&gt;

&lt;p&gt;Mais, pas d'inquiétude, on va pouvoir arranger ça.&lt;/p&gt;

&lt;p&gt;Commençons par noter, que pour la première fois depuis le début de l'article nous allons avoir besoin de quelque chose pour maintenir l'état. &lt;/p&gt;

&lt;p&gt;Et comme l'API n'est pas trop mal faite, dans un &lt;code&gt;Gatherer&lt;/code&gt;, le nom de ce concept est &lt;code&gt;state&lt;/code&gt;. ¯\&lt;em&gt;(ツ)&lt;/em&gt;/¯&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;org.github.jtama.gatherornot&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.stream.Gatherer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.stream.Gatherer.Integrator&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WithIndex&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Gatherer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Counting&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;withIndex&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Gatherer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSequential&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="err"&gt;③&lt;/span&gt;
            &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Counting&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
            &lt;span class="nc"&gt;Integrator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofGreedy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
             &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="o"&gt;++,&lt;/span&gt; &lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;))));&lt;/span&gt; &lt;span class="err"&gt;②&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counting&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;① Notre &lt;code&gt;Supplier&lt;/code&gt; d'état, ici une instance de la classe counting qui contient l'index&lt;br&gt;
② L'implémentation est exactement la même&lt;br&gt;
③ On utilise la méthode &lt;code&gt;ofSequential&lt;/code&gt;, qui permet d'interdire l'exécution du &lt;code&gt;gatherer&lt;/code&gt; en parallel, même si le développeur le demande.&lt;/p&gt;

&lt;p&gt;&lt;a id="Grouping"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Un &lt;code&gt;groupingBy&lt;/code&gt;, mais pas terminal.
&lt;/h3&gt;

&lt;p&gt;Pour rappel, la méthode &lt;code&gt;groupingBy&lt;/code&gt; est une opération finale, je vous encourage à aller lire &lt;a href="https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/util/stream/Collectors.html#groupingBy(java.util.function.Function)" rel="noopener noreferrer"&gt;la doc si vous voulez en savoir plus&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Ce que l'on cherche à implémenter c'est une opération intermédiaire qui va regrouper un ensemble cohérent d'élément dans une liste avant de les relacher dans le stream.&lt;/p&gt;

&lt;p&gt;Par exemple avec un stream contenant les œuvres de &lt;em&gt;Shakespeare&lt;/em&gt; classées par date de parution, j'aimerai pouvoir regrouper les oeuvres par année. Et tant qu'on y est, j'aimerais pouvoir l'utiliser pour d'autres objets, avec d'autres critères de regroupement.&lt;/p&gt;

&lt;p&gt;Cela signifie que nous allons faire un &lt;code&gt;Gatherer&lt;/code&gt; générique.&lt;/p&gt;

&lt;p&gt;Pour une fois, on va commencer en regardant l'utilisation !&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;oeuvres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;oeuvres&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;gather&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;series&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Oeuvre:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;anneeParution&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;&lt;span class="err"&gt;①&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;① Je passe à ma factory de &lt;code&gt;gatherer&lt;/code&gt; un extracteur de clef.&lt;/p&gt;

&lt;p&gt;Et maintenant l'implémentation :&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;org.github.jtama.gatherornot&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.ArrayList&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.List&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.function.Function&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.stream.Gatherer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Serie&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Gatherer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;series&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;keyExtractor&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Gatherer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSequential&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;①&lt;/span&gt;
                &lt;span class="nl"&gt;State:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="err"&gt;②&lt;/span&gt;
                &lt;span class="nc"&gt;Gatherer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Integrator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofGreedy&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// First invocation or the same key value&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; 
                            &lt;span class="n"&gt;keyExtractor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt;③&lt;/span&gt;
                        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keyExtractor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;
                    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="err"&gt;④&lt;/span&gt; 
                    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
                    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keyExtractor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                    &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                &lt;span class="o"&gt;}),&lt;/span&gt;
                &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="no"&gt;K&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;① Oui on va rester en séquentiel, sinon il faudra s'asseoir sur le fait d'avoir de la donnée ordonnée.&lt;br&gt;
② L'état va maintenir la clef de regroupement et la liste pour l'accumulation des valeurs regroupées.&lt;br&gt;
③ Si c'est le premier tour de boucle ou que la valeur de regroupement est égale à celle de l'état, on accumule et réclame plus d'éléments.&lt;br&gt;
④ Sinon, on pousse les valeurs déjà accumulées en conservant , et puis on reinitialise l'état et on propage le retour de l'invocation de la méthode &lt;code&gt;downstream.push&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Je me rend bien compte que ce &lt;code&gt;gatherer&lt;/code&gt; est un peu plus compliqué, mais l'avantage, c'est que si je veux regrouper mes éléments en fonction de la première lettre du titre, je peux.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;oeuvres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;oeuvres&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sorted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Comparator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;comparing&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Oeuvre:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;titre&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;gather&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;series&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oeuvre&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;oeuvre&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;titre&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Et ça fonctionnerait même avec une hypothétique classe &lt;code&gt;Person&lt;/code&gt;! Si je veux regrouper un &lt;code&gt;stream&lt;/code&gt; de personne par année de naissance :&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;persons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;persons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;gather&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;series&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Person:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;birthDate&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a id="Merge"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Et maintenant, on fusionne des &lt;code&gt;streams&lt;/code&gt; !
&lt;/h3&gt;

&lt;p&gt;Il n'est pas possible, simplement, à ce jour de fusionner des &lt;code&gt;stream&lt;/code&gt;. Ce que je veux je veux obtenir est l'équivalent du &lt;a href="https://reactivex.io/documentation/operators/join.html" rel="noopener noreferrer"&gt;&lt;code&gt;join&lt;/code&gt; de &lt;em&gt;RxJava&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mais en plus strict.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk39zm4yfco6uyfda0jwl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk39zm4yfco6uyfda0jwl.png" alt="Illustration du résultat souhaité" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà comme ça.&lt;/p&gt;

&lt;p&gt;Plus précisément je ne veux permettre que des paires complètes.&lt;/p&gt;

&lt;p&gt;On va reprendre le principe de commencer par l'utilisation.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Oeuvre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;oeuvres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readUnordered&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;prettyPrint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oeuvres&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;gather&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;merge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;streamToBeMerged&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;
  Le code ci-dessus devrait produire :
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;┌──────────────────────────┬─────────────────────────┐
│Revue de presse           │Titre                    │
├──────────────────────────┼─────────────────────────┤
│Beaucoup  de  bruits  pour│Peines d amour gagnées   │
│rien                      │                         │
├──────────────────────────┼─────────────────────────┤
│Je ne m en  souviens  même│Cardenio                 │
│plus.                     │                         │
├──────────────────────────┼─────────────────────────┤
│Jamais entendu parler     │La Tempête               │
├──────────────────────────┼─────────────────────────┤
│Numéro  1  sept   semaines│Les Deux Gentilshommes de│
│d affilées                │Vérone                   │
├──────────────────────────┼─────────────────────────┤
│Meilleure pièce de l année│Les Joyeuses Commères  de│
│                          │Windsor                  │
├──────────────────────────┼─────────────────────────┤
│Un chef d oeuvre          │Mesure pour mesure       │
└──────────────────────────┴─────────────────────────┘

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;Et maintenant l'implémentation :&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;org.github.jtama.gatherornot&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Iterator&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.stream.Gatherer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.stream.Stream&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Merge&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="no"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Gatherer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Iterator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="nc"&gt;Tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Y&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Gatherer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSequential&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nl"&gt;stream:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;iterator&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
            &lt;span class="nc"&gt;Gatherer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Integrator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
             &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasNext&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="err"&gt;②&lt;/span&gt;
                   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;downstream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tuple&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="err"&gt;③&lt;/span&gt;
              &lt;span class="o"&gt;}));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;① Pour savoir si il me reste quelque chose dans mon stream &lt;em&gt;"à fusionner"&lt;/em&gt; je ne peux directement faire un &lt;code&gt;Stream.hasNext&lt;/code&gt;, ou &lt;code&gt;Stream.next&lt;/code&gt; (Cette notion n'est pas cohérente la philosophie &lt;code&gt;Stream&lt;/code&gt;). Je dois avoir un accès direct aux éléments de la collection. Je dois passer par un &lt;code&gt;Iterator&lt;/code&gt;.&lt;br&gt;
② Si mon itérateur &lt;em&gt;"à fusionner"&lt;/em&gt; en a encore dans le ventre, on pousse au dowstream.&lt;br&gt;
③ Sinon on interrompt la consommation du &lt;code&gt;stream&lt;/code&gt;. Je rappelle qu'on a dit qu'on ne voulait que des paires complètes. Et puis c'est mon code, et je fais ce que je veux. Si vous voulez une autre implémentation, je ne vous empêche pas.&lt;/p&gt;
&lt;h2&gt;
  
  
  On y va ou pas ?
&lt;/h2&gt;

&lt;p&gt;Alors que cet article touche à sa fin, j'espère vous avoir montré qu'il existe en effet des cas pour lesquels les &lt;code&gt;Gatherer&lt;/code&gt; vont nous permettre de répondre à de réels besoins. J'aimerais aussi attirer votre attention sur le fait que si ils ont mis autant de temps à arriver, c'est certainement parce qu'on peut déjà faire beaucoup avec l'existant, pourvu qu'on prenne le temps de regarder ce que l'on a déjà à disposition.&lt;/p&gt;

&lt;p&gt;Vous trouverez dans le dépôt github joint, tout le code présenté et même plus.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/jtama" rel="noopener noreferrer"&gt;
        jtama
      &lt;/a&gt; / &lt;a href="https://github.com/jtama/to-gather-or-not-to-gather" rel="noopener noreferrer"&gt;
        to-gather-or-not-to-gather
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="adoc"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;To Gather, or not to Gather, that is the question.&lt;/h1&gt;

&lt;/div&gt;

&lt;div id="user-content-preamble"&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p&gt;&lt;span&gt;&lt;a rel="noopener noreferrer" href="https://github.com/jtama/to-gather-or-not-to-gatherslides/images/title_image.avif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fjtama%2Fto-gather-or-not-to-gatherslides%2Fimages%2Ftitle_image.avif" alt="title image"&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;

&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 id="user-content-run-locally" class="heading-element"&gt;Run locally&lt;/h2&gt;

&lt;/div&gt;

&lt;div&gt;
&lt;div&gt;
&lt;p&gt;Use JBang !&lt;/p&gt;
&lt;/div&gt;

&lt;div&gt;
&lt;p&gt;&lt;span&gt;&lt;a href="https://www.jbang.dev/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0f7e74a7d7bb843c6f6980ae541a3170d76265893def9663c74abbaa126de639/68747470733a2f2f7777772e6a62616e672e6465762f6173736574732f696d616765732f6c6f676f2e706e67" alt="Jbang logo"&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;

&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 id="user-content-slides" class="heading-element"&gt;Slides&lt;/h2&gt;

&lt;/div&gt;

&lt;div&gt;
&lt;div&gt;
&lt;p&gt;Readable version available on &lt;a href="https://jtama.github.io/https://github.com/jtama/to-gather-or-not-to-gather/#/" rel="nofollow noopener noreferrer"&gt;Github Pages → ici&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 id="user-content-generate" class="heading-element"&gt;Generate&lt;/h3&gt;

&lt;/div&gt;

&lt;div&gt;
&lt;div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;jbang qrcode@maxandersen -i slides/images/qr_inlay.png &lt;span class="pl-k"&gt;&amp;lt;&lt;/span&gt;open feedback url&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt; --qr-colo=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;linear-gradient(90deg, rgba(36,14,0,1) 0%, rgba(9,121,105,1) 35%, rgba(0,212,255,1) 100%);&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
jbang qrcode@maxandersen -i slides/image/github.png https://github.com/jtama/to-gather-or-not-to-gather
podman container run --rm -v &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;$(&lt;/span&gt;pwd&lt;span class="pl-pds"&gt;)&lt;/span&gt;&lt;/span&gt;/slides:/documents -w /documents asciidoctor/docker-asciidoctor:1.80.0 asciidoctor-revealjs -r asciidoctor-diagram index.adoc&lt;/pre&gt;

&lt;/div&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;

&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 id="user-content-run-locally-1" class="heading-element"&gt;Run locally&lt;/h3&gt;

&lt;/div&gt;

&lt;div&gt;
&lt;div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;podman container run --name prez --rm -d -p 8080:80 -v &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;$(&lt;/span&gt;pwd&lt;span class="pl-pds"&gt;)&lt;/span&gt;&lt;/span&gt;/slides:/usr/share/nginx/html nginx
podman container run --name coder --rm -d -p 8443:8443 -v &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;$(&lt;/span&gt;pwd&lt;span class="pl-pds"&gt;)&lt;/span&gt;&lt;/span&gt;/app:/config/workspace ghcr.io/jtama/java_jbang_codeserver:latest&lt;/pre&gt;

&lt;/div&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/jtama/to-gather-or-not-to-gather" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Et surtout n'oubliez pas : &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F89jg0fa71rsz387n9y1i.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F89jg0fa71rsz387n9y1i.jpg" alt="Rester curieux" width="612" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>stream</category>
      <category>gatherer</category>
      <category>french</category>
    </item>
    <item>
      <title>OpenRewrite: Refactoring as code</title>
      <dc:creator>Kosmik</dc:creator>
      <pubDate>Fri, 07 Feb 2025 09:39:44 +0000</pubDate>
      <link>https://forem.com/onepoint/openrewrite-refactoring-as-code-2p48</link>
      <guid>https://forem.com/onepoint/openrewrite-refactoring-as-code-2p48</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;📌 NOTE &lt;strong&gt;Mieux vaut prévenir que guérir&lt;/strong&gt;, je n’utiliserai pas le mot &lt;strong&gt;reusiner&lt;/strong&gt;, et certainement pas non plus le mot &lt;em&gt;refactoriser&lt;/em&gt;. Cette note sera donc pleine d’anglicismes. La vie.&lt;br&gt;
Il y aura aussi du code tronqué, mais pas d’inquiétude, il y a un lien à la fin de l’article vers le dépôt qui contient le code complet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alors comme ça vous voulez &lt;em&gt;refactorer&lt;/em&gt; votre code ? Voici les différentes options qui s’offrent habituellement à vous (je sais, il y en a d’autres) :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pour commencer le fameux (ou l’infâme) &lt;strong&gt;Chercher/Remplacer&lt;/strong&gt;, plus connu sous le nom de &lt;code&gt;Ctrl+F/Ctrl+R&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;La technique un peu plus avancée de l’expression régulière, aussi connue sour le nom de &lt;em&gt;Les regexps c’est illisible 15 jours après les avoir écrites&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Mais de façon plus probable, vous utiliserez le menu &lt;em&gt;click droit&lt;/em&gt; de votre IDE, ou un bon paquet de raccourcis clavier, technique également connue sous le nom de &lt;em&gt;tu peux toujours courir, jamais tu reproduiras ce que j’ai fait&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si vous êtes dans le cas d’un refactoring qui nécessite plus d’une étape et/ou qui touche à de nombreux fichiers, vous êtes très probablement condamné à suivre un guide de migration, aussi triste qu’un long dimanche de pluie.&lt;/p&gt;

&lt;p&gt;Disons que vous vouliez par exemple migrer de &lt;code&gt;JUnit 4&lt;/code&gt; à &lt;code&gt;JUnit 5&lt;/code&gt;, ou bien de &lt;code&gt;Spring Boot 2&lt;/code&gt; à &lt;code&gt;Spring Boot 4&lt;/code&gt;, ou  d'&lt;code&gt;Hibernate 4&lt;/code&gt; à &lt;code&gt;Hibernate 6&lt;/code&gt;, ou encore de &lt;code&gt;Java 8&lt;/code&gt; à &lt;code&gt;Java 21&lt;/code&gt;. Il vous faudrait pour cela :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Faire les montées de versions dans vos fichiers de configuration &lt;code&gt;Maven&lt;/code&gt; ou &lt;code&gt;Gradle&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Changer vos imports&lt;/li&gt;
&lt;li&gt;Changer vos annotations&lt;/li&gt;
&lt;li&gt;Changer vos invocations de méthodes&lt;/li&gt;
&lt;li&gt;Peut-être changer vos signature de méthodes&lt;/li&gt;
&lt;li&gt;Changer les noms de propriété&lt;/li&gt;
&lt;li&gt;Je ne sais quoi encore&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;C’est chronophage, lourd, sujet aux erreurs et aux oublis.&lt;/p&gt;

&lt;p&gt;Mais il y a un petit nouveau dans le monde du refactoring : &lt;strong&gt;OpenRewrite&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The big picture 📸
&lt;/h2&gt;

&lt;p&gt;OpenRewrite est un outil de refactoring qui a été créé par &lt;a href="https://www.moderne.ai" rel="noopener noreferrer"&gt;Moderne&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Il a commencé avec un fort accent sur Java (et ses fichiers de configuration : properties et yaml), mais il s’étend maintenant à d’autres langages et formats de fichiers :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Langages de programmation : Java, Kotlin, Groovy&lt;/li&gt;
&lt;li&gt;Formats de données : XML, Properties, YAML, JSON, Protobuf&lt;/li&gt;
&lt;li&gt;Outils de build : Maven, Gradle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Je me concentrerai uniquement sur l’écosystème Java dans cet article, même si des migrations existent pour &lt;em&gt;Kubernetes&lt;/em&gt;, &lt;em&gt;AWS&lt;/em&gt;, &lt;em&gt;.Net&lt;/em&gt;, &lt;em&gt;CircleCI&lt;/em&gt;, &lt;em&gt;Github Actions&lt;/em&gt; et encore bien d’autres.&lt;/p&gt;

&lt;h2&gt;
  
  
  Les concepts
&lt;/h2&gt;

&lt;p&gt;Les principaux concepts d’OpenRewrite que vous devez garder à l’esprit sont les suivants.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;Lossless Syntax Tree&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Le code source que vous souhaitez consulter et transformer est rendu accessible via un &lt;strong&gt;&lt;em&gt;LST&lt;/em&gt;&lt;/strong&gt;. C’est un arbre de syntax abstrait (&lt;strong&gt;AST&lt;/strong&gt;) contenant en plus des informations de formatage. Une représentation asbtraite du code source pour le rendre plus facile à comprendre, à interroger ou à manipuler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recette 🍴
&lt;/h3&gt;

&lt;p&gt;Ou &lt;em&gt;recipe&lt;/em&gt; dans la langue de Shakespeare est l’élément atomique pour travailler sur le code. Elle peut contenir chacun des éléments suivants (tous étant optionnels):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un/des paramètre(s) pour personnaliser le comportement de la recette.&lt;/li&gt;
&lt;li&gt;Une liste de pré-conditions, lui permettant de savoir si elle doit traiter un fichier source.&lt;/li&gt;
&lt;li&gt;Un &lt;em&gt;visitor&lt;/em&gt; qui permet d’effectuer ou non des modifications sur le code source.&lt;/li&gt;
&lt;li&gt;Une liste de recettes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ce dernier point signifie que la façon d’implémenter un refactoring complexe est de composer des &lt;em&gt;recettes&lt;/em&gt;, et à la fin, cela s’appelle aussi une recette.&lt;/p&gt;

&lt;p&gt;¯\&lt;em&gt;(ツ)&lt;/em&gt;/¯ Ne blâmez pas le messager.&lt;/p&gt;

&lt;h3&gt;
  
  
  Le pattern visitor 👾
&lt;/h3&gt;

&lt;p&gt;Je l’ai mentionné juste au-dessus, mais le traitement d’une recette passe par le pattern visitor. Chaque rencontre d’un élément du &lt;em&gt;lst&lt;/em&gt; va générer un &lt;strong&gt;événement&lt;/strong&gt; qui sera transmis à tous les visiteurs déclarés. Pour vous donner une idée, il y a actuellement 73 types d’événements différents associés à la &lt;em&gt;visite&lt;/em&gt; pour du code java, chacun associé à des méthodes avec des signatures différentes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qu'y a-t-il dans la boîte 📦 ?
&lt;/h2&gt;

&lt;p&gt;OpenRewrite est plus qu’un framework monolithique. Ces différents composants sont :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un module &lt;em&gt;core&lt;/em&gt;, qui contient toute la représentation générique d’un LST et la logique de refactoring commune.&lt;/li&gt;
&lt;li&gt;Un module pour chaque langage, avec des API et SDK dédiés pour une cible spécifique (Java, XML, Yaml, etc).&lt;/li&gt;
&lt;li&gt;De nombreux modules contenant des recettes pour un sous-ensemble d’intérêt spécifique, telles que les recettes de frameworks de test, les recettes de Spring, les recettes de Quarkus, l’identification et la correction des problèmes d’analyse statique, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Le catalogue 📓
&lt;/h2&gt;

&lt;p&gt;La grande puissance d’Openrewrite est la mise à disposition de recettes déjà disponible pour traiter un très grand nombre de cas sans que l’on ait à produire la moindre ligne de code.&lt;br&gt;
Il y a actuellement plus de 500 recettes disponibles pour Java, et il y en a probablement plus pour les autres langages et formats de fichiers.&lt;/p&gt;

&lt;p&gt;Le catalogue est disponible en ligne à l’adresse suivante : &lt;a href="https://docs.openrewrite.org/recipes" rel="noopener noreferrer"&gt;https://docs.openrewrite.org/recipes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Il contient toutes les recettes produites par l’équipe de Moderne, triées par catégories, documentées et avec des exemples d’utilisation. Mais il contient également d’autres sections intéressantes. On y trouve par exemple une liste des recettes &lt;a href="https://docs.openrewrite.org/popular-recipe-guides" rel="noopener noreferrer"&gt;les plus populaires&lt;/a&gt;, comme un accélérateur de recherche, mais aussi une page dédiée &lt;a href="https://docs.openrewrite.org/reference/community-recipes" rel="noopener noreferrer"&gt;aux recettes écrites par d’autre&lt;/a&gt;, comme &lt;em&gt;Apache Camel&lt;/em&gt;, &lt;em&gt;AWS&lt;/em&gt;, &lt;em&gt;Quarkus&lt;/em&gt;, &lt;em&gt;Morphia&lt;/em&gt; et bien d’autres.&lt;/p&gt;
&lt;h2&gt;
  
  
  Execution d'une recette ⚙️
&lt;/h2&gt;

&lt;p&gt;Attaquons-nous à la partie la plus simple de cet article : l’exécution d’une recette. Petit prérequis: vous devez avoir &lt;code&gt;Maven&lt;/code&gt; ou &lt;code&gt;Gradle&lt;/code&gt; installé sur votre machine. Que vous vouliez exécuter une recette qui concerne &lt;em&gt;Java&lt;/em&gt; ou &lt;em&gt;Kubernetes&lt;/em&gt;, la procédure est la même. Désolé.&lt;/p&gt;

&lt;p&gt;Pour exécuter une recette, vous avez deux options.&lt;/p&gt;
&lt;h3&gt;
  
  
  En modifiant vos descripteurs de build
&lt;/h3&gt;

&lt;p&gt;Je vais prendre l’exemple d’un projet &lt;em&gt;Maven&lt;/em&gt;, mais les étapes à suivre sont les mêmes pour un projet &lt;em&gt;Gradle&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Pour commencer, vous devez ajouter le plugin &lt;code&gt;rewrite-maven-plugin&lt;/code&gt; à votre fichier &lt;code&gt;pom.xml&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;1&amp;gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.openrewrite.maven&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;rewrite-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.46.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;2&amp;gt;
    &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Déclaration du plugin&lt;/li&gt;
&lt;li&gt;Adapter le numéro pour utiliser la version la plus à jour&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ensuite, vous devez déclarer la recette que vous voulez exécuter. Ici par exemple la suppression de &lt;em&gt;Cobertura&lt;/em&gt; qui n’est plus compatible avec un projet &lt;em&gt;Java&lt;/em&gt; dont la version est supérieure à &lt;em&gt;Java 11&lt;/em&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.openrewrite.maven&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;rewrite-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.46.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt; ①
          &lt;span class="nt"&gt;&amp;lt;activeRecipes&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;recipe&amp;gt;&lt;/span&gt;org.openrewrite.java.migrate.cobertura.RemoveCoberturaMavenPlugin&lt;span class="nt"&gt;&amp;lt;/recipe&amp;gt;&lt;/span&gt; ②
          &lt;span class="nt"&gt;&amp;lt;/activeRecipes&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Configuration du plugin&lt;/li&gt;
&lt;li&gt;Activation de la recette&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ajout de la dépendance dans laquelle se trouve la recette (si elle n’est pas dans le module core), ce qui donne la configuration complète suivante :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.openrewrite.maven&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;rewrite-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.46.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;activeRecipes&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;recipe&amp;gt;&lt;/span&gt;org.openrewrite.java.migrate.cobertura.RemoveCoberturaMavenPlugin&lt;span class="nt"&gt;&amp;lt;/recipe&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/activeRecipes&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.openrewrite.recipe&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;rewrite-migrate-java&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.30.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pour exécuter la recette, il suffit de lancer la commande suivante :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mvn rewrite:run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mais on ne veut pas modifier nos fichiers de build, n’est-ce pas ? Et on ne se trouve peut-être même pas dans un projet &lt;em&gt;Maven&lt;/em&gt; ou &lt;em&gt;Gradle&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sans modifier vos descripteurs de build
&lt;/h3&gt;

&lt;p&gt;Dans ce cas, il est possible de préciser directement tout dans la ligne de commande, mais celle-ci deviendra forcément plus complexe :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mvn &lt;span class="nt"&gt;-U&lt;/span&gt; org.openrewrite.maven:rewrite-maven-plugin:run &amp;lt;1&amp;gt;
&lt;span class="go"&gt;   -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-java:2.30.1 ②
   -Drewrite.activeRecipes=org.openrewrite.java.migrate.cobertura.RemoveCoberturaMavenPlugin ③
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Déclaration du plugin&lt;/li&gt;
&lt;li&gt;Ajout de la dépendance de la recette&lt;/li&gt;
&lt;li&gt;Activation de la recette&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Intégration avec IntelliJ IDEA 🚀
&lt;/h3&gt;

&lt;p&gt;Si vous utilisez &lt;em&gt;IntelliJ IDEA&lt;/em&gt; au quotidien, il est possible d’exécuter une recette directement depuis l’IDE. Pour cela, il vous suffit de cliquer sur l’icône icon:play[] présent à la gauche de la recette que vous voulez exécuter.&lt;/p&gt;

&lt;p&gt;Les principales fonctionnalités sont :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pas besoin de faire de configuration maven\gradle&lt;/li&gt;
&lt;li&gt;Pas besoin de connaître la ligne de commande&lt;/li&gt;
&lt;li&gt;Autocompletion lors de la conception de recettes déclaratives&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Concevoir ses propres recettes
&lt;/h2&gt;

&lt;p&gt;Les façons de faire décrites ci-dessus ne sont valables que si les recettes ne prevous en avez besoin, il va falloir passer à l’étape suivante : la conception de recettes.&lt;/p&gt;

&lt;p&gt;Pour concevoir ses propres recettes, le guide de bonne pratique d’Openrewrite nous dit que tout ce qui peut être faît de manière déclarative doit l’être. Oui, je sais, c’est dur. Vous êtes des développeurs, vous voulez écrire du code. Mais c’est comme ça.&lt;/p&gt;

&lt;p&gt;Openrewrite nous offre pour cela un format de déclaration de recette en &lt;em&gt;YAML&lt;/em&gt;. Oh oui youpiiiii 💃.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recette déclarative (&lt;em&gt;Declarative recipe&lt;/em&gt;)
&lt;/h2&gt;

&lt;p&gt;Le format proposé par Openrewrite pour recette déclarative permet d’assigner une sous partie de ce qui est possible en Java. Il n’est notamment pas possible d’ajouter des paramètres, ni de renvoyer un visiteur dans une recette déclarative.&lt;/p&gt;

&lt;p&gt;Voici un exemple de recette déclarative qui supprime la dépendance &lt;code&gt;com.github.jtama:toxic&lt;/code&gt; d’un projet &lt;em&gt;Maven&lt;/em&gt;. La recette doit-être écrite dans un fichier s’appelant &lt;code&gt;rewrite.yml&lt;/code&gt; et se trouvant soit à la racine du projet, soit dans le répertoire &lt;code&gt;META-INF/rewrite&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;specs.openrewrite.org/v1beta/recipe ①&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.github.jtama.openrewrite.RemovesThatToxicDependency ②&lt;/span&gt;
&lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Removes that toxic dependency ③&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
  &lt;span class="s"&gt;Migrate from AcmeToxic ☠️ to AcmeHealthy 😇,&lt;/span&gt;
  &lt;span class="s"&gt;removes dependencies and migrates code.  ④&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;⑤&lt;/span&gt;
  &lt;span class="s"&gt;- acme&lt;/span&gt;
  &lt;span class="s"&gt;- toxic&lt;/span&gt;
&lt;span class="na"&gt;recipeList&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;⑥&lt;/span&gt;
  &lt;span class="s"&gt;- org.openrewrite.java.ChangeMethodTargetToStatic&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;⑦&lt;/span&gt;
      &lt;span class="s"&gt;methodPattern&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.github.jtama.toxic.toxic.BigDecimalUtils valueOf(..)&lt;/span&gt;
      &lt;span class="s"&gt;fullyQualifiedTargetTypeName&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;java.math.BigDecimal&lt;/span&gt;
  &lt;span class="s"&gt;- org.openrewrite.maven.RemoveDependency&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;groupId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.github.jtama&lt;/span&gt;
      &lt;span class="na"&gt;artifactId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;toxic-library&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;org.openrewrite.maven.RemoveUnusedProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;propertyPattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.*toxic\.version&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;com.github.jtama.openrewrite.VousAllezVoirCeQueVousAllezVoir&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;specs.openrewrite.org/v1beta/recipe&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.github.jtama.openrewrite.VousAllezVoirCeQueVousAllezVoir&lt;/span&gt;
&lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ça va vous épater&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
  &lt;span class="s"&gt;Rech. proj. pr proj. priv. Self Dem. Brt. Poss. S’adr. à l’hô. Mart&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;acme&lt;/span&gt;
&lt;span class="na"&gt;preconditions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;org.openrewrite.text.Find&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;⑧&lt;/span&gt;
      &lt;span class="s"&gt;find&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.github.jtama&lt;/span&gt;
&lt;span class="na"&gt;recipeList&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;com.github.jtama.openrewrite.RemoveFooBarUtilsIsEmpty&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;com.github.jtama.openrewrite.RemoveFooBarUtilsStringFormatted&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;com.github.jtama.openrewrite.UseObjectsCompare&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Déclaration du type de recette&lt;/li&gt;
&lt;li&gt;Nom de la recette&lt;/li&gt;
&lt;li&gt;Nom affiché lors de l’exécution de la recette&lt;/li&gt;
&lt;li&gt;Description de la recette&lt;/li&gt;
&lt;li&gt;Tags pour faciliter la recherche&lt;/li&gt;
&lt;li&gt;Liste des recettes à exécuter&lt;/li&gt;
&lt;li&gt;Passage de paramètre à une recette&lt;/li&gt;
&lt;li&gt;Un exemple de précondition. ⚠️ Attention cette précondition va s’exécuter pour toutes les recettes de la liste.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Comme nous l’avons vu dans l’exemple précédent, cela permet de construire des recettes complexes en les composant les unes avec les autres.&lt;/p&gt;

&lt;p&gt;Deux points d’attention sont à noter :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Le fichier doit s’appeler &lt;code&gt;rewrite.yml&lt;/code&gt;, pas &lt;code&gt;rewrite.yaml&lt;/code&gt;. 🙄&lt;/li&gt;
&lt;li&gt;Pour que cette recette puisse s’exécuter, les 3 recettes filles doivent être accessibles dans le &lt;em&gt;classpath&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mvn &lt;span class="nt"&gt;-U&lt;/span&gt; org.openrewrite.maven:rewrite-maven-plugin:run &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="go"&gt;   -Drewrite.recipeArtifactCoordinates=com.github.jtama:toxic-library-remover:1.0.0 \
   -Drewrite.activeRecipes=com.github.jtama.openrewrite.RemovesThatToxicDependency
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Distribution
&lt;/h3&gt;

&lt;p&gt;Vous êtes heureux de ce que vous avez fait, vous voulez partager votre recette avec le monde entier. Pour cela, il vous suffit de créer un module &lt;em&gt;Maven&lt;/em&gt; ou &lt;em&gt;Gradle&lt;/em&gt; et de le publier. Chacun pourra dès lors utiliser à loisir votre recette.&lt;/p&gt;

&lt;p&gt;Le projet devra comprendre le fichier &lt;code&gt;rewrite.yml&lt;/code&gt; et les dépendances nécessaires pour que la recette puisse s’exécuter.&lt;/p&gt;

&lt;h2&gt;
  
  
  On code nos recettes ✒️
&lt;/h2&gt;

&lt;p&gt;Pour les chapitres suivants, nous partons du principe que vous voulez vous débarrasser d’une dépendance toxique (com.github.jtama:toxic-library:19.666.45-RC18-FINAL) qui comprend les classes suivantes :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.github.jtama.toxic&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Comparator&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.List&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FooBarUtils&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;stringFormatted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;compare&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt; &lt;span class="n"&gt;o1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;T&lt;/span&gt; &lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Comparator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;comparator&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;comparator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compare&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.github.jtama.toxic&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.math.BigDecimal&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BigDecimalUtils&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="nf"&gt;valueOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On ne se pose pas de question le code en lui-même, dîtes-vous que c’est un axiome.&lt;/p&gt;

&lt;p&gt;Nous allons mettre en œuvre 2 types de recettes :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Refaster template recipes&lt;/code&gt;, ou recettes &lt;em&gt;refaster&lt;/em&gt;. Simples, mais limitées.&lt;/li&gt;
&lt;li&gt;Full custom java recipes (Bam ! Pas un seul mot français).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Refaster template recipes ⚡️
&lt;/h3&gt;

&lt;p&gt;Ces patrons de recettes utilisent &lt;a href="https://errorprone.info/docs/refaster" rel="noopener noreferrer"&gt;&lt;em&gt;refaster&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Elles permettent de décrire simplement des templates recettes via du code. L’outillage &lt;em&gt;OpenRewrite&lt;/em&gt; génère ensuite les recettes complètes à partir de ces templates.&lt;/p&gt;

&lt;p&gt;Pour les utiliser il vous faut ajouter les dépendances suivantes à votre projet. Le code suivant est un copier/coller &lt;a href="https://docs.openrewrite.org/authoring-recipes/refaster-recipes#update-your-dependencies" rel="noopener noreferrer"&gt;de la documentation officielle&lt;/a&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Refaster style recipes need the rewrite-templating annotation processor and dependency for generated recipes --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.openrewrite&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;rewrite-templating&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- If you are developing recipes in Java, you'll need to bring in rewrite-java --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.openrewrite&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;rewrite-java&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- The `@BeforeTemplate` and `@AfterTemplate` annotations are needed for refaster style recipes --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.google.errorprone&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;error_prone_core&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.19.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;provided&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;exclusions&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;exclusion&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.google.auto.service&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;auto-service-annotations&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/exclusion&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/exclusions&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.maven.plugins&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;maven-compiler-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.12.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;source&amp;gt;&lt;/span&gt;17&lt;span class="nt"&gt;&amp;lt;/source&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;target&amp;gt;&lt;/span&gt;17&lt;span class="nt"&gt;&amp;lt;/target&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;compilerArgs&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;arg&amp;gt;&lt;/span&gt;-parameters&lt;span class="nt"&gt;&amp;lt;/arg&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/compilerArgs&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;annotationProcessorPaths&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;path&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.projectlombok&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;lombok&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.18.32&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/path&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;path&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.openrewrite&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;rewrite-templating&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.19.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/path&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/annotationProcessorPaths&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous pouvons maintenant créer une classe qui va supprimer les invocations des méthodes &lt;code&gt;FooBarUtils.isEmpty&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RecipeDescriptor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Replace `FooBarUtils.isEmptyString(String)` with standard equivalent"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Replace `FooBarUtils.isEmptyString(String)` with ternary 'value == null || value.isEmpty()'."&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RemoveStringIsEmpty&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nd"&gt;@BeforeTemplate&lt;/span&gt;
        &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;FooBarUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nd"&gt;@AfterTemplate&lt;/span&gt;
        &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;after&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Le nom et la description de la recette&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Les annotations @BeforeTemplate et @AfterTemplate permettent de marquer les méthodes qui seront utilisées pour générer respectivement le template permettant de trouver les invocations à modifier et le template permettant de générer le code de remplacement.&lt;/p&gt;

&lt;p&gt;Le deux méthodes doivent avoir le même nombre de paramètres avec les mêmes types et noms.&lt;/p&gt;

&lt;p&gt;Il est possible de grouper les templates de recettes refaster comme suit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.github.jtama.openrewrite&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.github.jtama.toxic.FooBarUtils&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.errorprone.refaster.annotation.AfterTemplate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.errorprone.refaster.annotation.BeforeTemplate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.openrewrite.java.template.RecipeDescriptor&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.List&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@RecipeDescriptor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Remove `FooBarUtils.isEmpty` methodes usages"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Replace any usage of `FooBarUtils.isEMpty` method by standards equivalent."&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RemoveFooBarUtilsIsEmpty&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@RecipeDescriptor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Replace `FooBarUtils.isEmptyString(String)` with standard equivalent"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Replace `FooBarUtils.isEmptyString(String)` with ternary 'value == null || value.isEmpty()'."&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RemoveStringIsEmpty&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nd"&gt;@BeforeTemplate&lt;/span&gt;
        &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;FooBarUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nd"&gt;@AfterTemplate&lt;/span&gt;
        &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;after&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;


    &lt;span class="nd"&gt;@RecipeDescriptor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Replace `FooBarUtils.isEmptyList(List)` with standard equivalent"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Replace `FooBarUtils.isEmptyList(List)` with ternary 'value == null || value.isEmpty()'."&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RemoveListIsEmpty&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nd"&gt;@BeforeTemplate&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;FooBarUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nd"&gt;@AfterTemplate&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;after&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dans ce cas, la recette &lt;code&gt;RemoveFooBarUtilsIsEmptyRecipes&lt;/code&gt; générée contiendra une liste de recette comprenant les recettes &lt;code&gt;RemoveStringIsEmptyRecipe&lt;/code&gt; et &lt;code&gt;RemoveListIsEmptyRecipe&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Dans les faits ce type de recette est relativement restreint. Le code ciblé doit pouvoir s’exprimer dans le bloc d’une méthode, et il sera toujours relativement simple et non paramètrable. Il ne pourra pas non plus retenir le style de formatage du code source d’origine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full custom java recipes ☕️
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Toujours pas de français&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;La recette suivante va remplacer les invocations de &lt;code&gt;FooBarUtils.stringFormatted(String, Object varargs)&lt;/code&gt; par des invocations de &lt;code&gt;String.format(Object varargs)&lt;/code&gt;. Celle-ci ne peut pas être réalisée avec un template, parce que le nombre de paramètres de ces méthodes ne peut être connu à l’avance.&lt;/p&gt;

&lt;p&gt;Nous allons donc devoir passer à l’étape supérieure.&lt;/p&gt;

&lt;p&gt;Toute recette doit étendre la classe &lt;code&gt;org.openrewrite.Recipe&lt;/code&gt;. Nous allons la construire petit à petit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;...&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RemoveFooBarUtilsStringFormatted&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Recipe&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getDisplayName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt;②&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Remove `FooBarUtils.stringFormatted`"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getDescription&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt;③&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Replace any usage of `FooBarUtils.stringFormatted` with `String.formatted` method."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Il y a évidemment beaucoup d’imports...&lt;/li&gt;
&lt;li&gt;Le nom affiché lors de l’exécution de la recette&lt;/li&gt;
&lt;li&gt;La description de la recette. Celle-ci DOIT finir par un point&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ajoutons maintenant la méthode qui retourne le visiteur.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;...&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RemoveFooBarUtilsStringFormatted&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Recipe&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getDisplayName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getDescription&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;TreeVisitor&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?,&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getVisitor&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Preconditions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Check&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UsesType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.github.jtama.toxic.FooBarUtils"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ToStringFormattedVisitor&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; &lt;span class="err"&gt;②&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;La classe &lt;code&gt;Preconditions.Check&lt;/code&gt; étend la classe &lt;code&gt;TreeVisitor&lt;/code&gt; et permet de vérifier si une condition est remplie avant de lancer le visiteur. Ici, je valide que le type &lt;code&gt;com.github.jtama.toxic.FooBarUtils&lt;/code&gt; est utilisé par une classe avant même de la &lt;em&gt;visiter&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;La classe &lt;code&gt;ToStringFormattedVisitor&lt;/code&gt; que nous allons créer pour effectuer les modifications sur le code source.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Il est maintenant temps de créer la classe &lt;code&gt;ToStringFormattedVisitor&lt;/code&gt; qui va effectuer les modifications sur le code source.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ToStringFormattedVisitor&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JavaIsoVisitor&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ExecutionContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;

        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;MethodMatcher&lt;/span&gt; &lt;span class="n"&gt;toxicStringFormatted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MethodMatcher&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.github.jtama.toxic.FooBarUtils stringFormatted(String,..)"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="err"&gt;②&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;JavaTemplate&lt;/span&gt; &lt;span class="n"&gt;stringFormatted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JavaTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"#\{any(java.lang.String)}.formatted()"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="err"&gt;③&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Ce visiteur va étendre la classe &lt;code&gt;JavaIsoVisitor&lt;/code&gt; qui va nous fournir tous les points d’extension pour du code &lt;code&gt;java&lt;/code&gt;, c’est une bonne base pour tout refactoring java.&lt;/li&gt;
&lt;li&gt;Le &lt;code&gt;MethodMatcher&lt;/code&gt; va permettre de matcher les invocations de la méthode &lt;code&gt;FooBarUtils#stringFormatted&lt;/code&gt;. Ici, il ne s’agit pas d’une simple expression régulière. Le framework va faire des comparaisons au niveau sémantique.&lt;/li&gt;
&lt;li&gt;Le &lt;code&gt;JavaTemplate&lt;/code&gt; va permettre de générer l’invocation attendue.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Il est en effet possible de créer des éléments de source programmatiquement, mais créer de l'&lt;strong&gt;AST&lt;/strong&gt; à la main est long et sujet à erreur, c’est possible, mais à vos risques et périls. Il est donc fortement déconseillé de le faire. Dans notre cas, je crée le template d’invocation minimal d’une méthode pour pouvoir le modifier ensuite.&lt;/p&gt;

&lt;p&gt;Comme je veux remplacer une invocation de méthode par une autre, je vais surcharger la méthode &lt;code&gt;JavaIsoVisitor#visitMethodInvocation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Commençons petit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ToStringFormattedVisitor&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JavaIsoVisitor&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ExecutionContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodInvocation&lt;/span&gt; &lt;span class="nf"&gt;visitMethodInvocation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodInvocation&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodInvocation&lt;/span&gt; &lt;span class="n"&gt;methodInvocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;visitMethodInvocation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;toxicStringFormatted&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matches&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;methodInvocation&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;methodInvocation&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;maybeRemoveImport&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.github.jtama.toxic.FooBarUtils"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;methodInvocation&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le premier paramètre représente l’invocation de méthode courante. Nous commençons par laisser le &lt;code&gt;JavaIsoVisitor&lt;/code&gt; faire son travail. Si l’invocation en cours ne correspond pas à ce que nous voulons modifier, nous la retournons sans faire de modification aucune.&lt;/p&gt;

&lt;p&gt;Sinon, c’est le début de l’action. Tout d’abord, nous utilisons une méthode utilitaire fournie par la classe &lt;code&gt;JavaVisitor&lt;/code&gt; qui nous permet de dire que si l’import &lt;code&gt;com.github.jtama.toxic.FooBarUtils&lt;/code&gt; n’est plus utile, il peut être supprimé.&lt;/p&gt;

&lt;p&gt;Et maintenant, nous allons générer le code  voulu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ToStringFormattedVisitor&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JavaIsoVisitor&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ExecutionContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodInvocation&lt;/span&gt; &lt;span class="nf"&gt;visitMethodInvocation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodInvocation&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ExecutionContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Expression&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;methodInvocation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getArguments&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="no"&gt;J&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodInvocation&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stringFormatted&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;apply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;getCursor&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;methodInvocation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCoordinates&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;mi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withArguments&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ListUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mapFirst&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;()),&lt;/span&gt;
                &lt;span class="n"&gt;expression&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withPrefix&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Space&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EMPTY&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Je commence par capter la liste des arguments de l’invocation de la méthode &lt;code&gt;FooBarUtils#stringFormatted&lt;/code&gt;. Je vais ensuite appliquer le template java avec le premier argument: c’est l’instance de &lt;code&gt;String&lt;/code&gt; qui contient le template de formattage.&lt;/p&gt;

&lt;p&gt;Le premier argument, le &lt;em&gt;curseur&lt;/em&gt;, peut-être vu comme un pointeur vers le code en cours de traitement dans l’arbre. Le deuxième argument, les &lt;em&gt;coordonnées&lt;/em&gt; permettent d’indiquer si on veut remplacer le code, en ajouter avant ou après.&lt;/p&gt;

&lt;p&gt;Enfin, je complète l’invocation de méthode en lui passant la liste des arguments restants. J’apporte néanmoins une petite modification, j’enlève tout espace au premier argument (c’est plus joli 😇).&lt;/p&gt;

&lt;h2&gt;
  
  
  Tester ses recettes 🧪
&lt;/h2&gt;

&lt;p&gt;Le framework Openrewrite nous offre tout le nécessaire pour tester nos recettes. Parmi les bonnes pratiques d’écriture que je n’ai pas suivies dans cet article, il est d’ailleurs précisé qu’une fois le squelette de la recette écrit, il est préférable de commencer par écrire le test. Oui, on parle bien &lt;strong&gt;Test Driven Development Refactoring As Code - TDDRAC&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Les classes de tests doivent implémenter l’interface &lt;code&gt;RewriteTest&lt;/code&gt;. Et c’est tout. Il existe une méthode &lt;code&gt;default&lt;/code&gt; que l’on peut surcharger et qui est l’équivalent d’un &lt;code&gt;beforeEach&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RemoveFooBarUtilsStringFormattedTest&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;RewriteTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;defaults&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RecipeSpec&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;recipe&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RemoveFooBarUtilsStringFormatted&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="err"&gt;①&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JavaParser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJavaVersion&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="err"&gt;②&lt;/span&gt;
              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logCompilationWarningsAndErrors&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;classpath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"toxic-library"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="err"&gt;③&lt;/span&gt;
           &lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;La recette que je veux exécuter&lt;/li&gt;
&lt;li&gt;La version de Java inférée à partir de la configuration du projet&lt;/li&gt;
&lt;li&gt;J’ajoute la dépendance &lt;code&gt;toxic-library&lt;/code&gt; au classpath du parser du test.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Et le code du test en lui-même est si clair, qu’il peut servir à documenter l’intention de la recette.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;removeStringFormattedInvocation&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;rewriteRun&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="c1"&gt;//language=java ①&lt;/span&gt;
      &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"""
          import com.github.jtama.toxic.FooBarUtils;

          public class FullDriftCar {

              public String foo() {
                  return new FooBarUtils().stringFormatted("Hello %s %s %s", 2L,
                     "tutu" +
                     "tata",
                     this.getClass()
                          .getName());
              }
          }
          """&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"""
          public class FullDriftCar {

              public String foo() {
                  return "Hello %s %s %s".formatted(2L,
                     "tutu" +
                     "tata",
                     this.getClass()
                          .getName());
              }
          }
          """&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Pour aider votre IDE avec la coloration syntaxique&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;La méthode &lt;code&gt;java&lt;/code&gt; prend deux paramètres, le code sur lequel on va exécuter la recette, et celui qui doit être produit.&lt;/p&gt;

&lt;p&gt;Comme vous le voyez, rédiger les tests ne pose aucune complexité. Et si certain cas sont un peu plus complexes, avec de la persévérance on y arrive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Il est temps de s’arrêter, et même si je n’ai fait que gratter la surface, j’espère vous avoir donné l’envie de prendre votre pelle pour aller plus loin.&lt;/p&gt;

&lt;p&gt;Comme toujours en informatique, OpenRewrite n’est pas la solution à tous les refactorings, et il serait très certainement exagéré de l’utiliser pour renommer une méthode d’une classe non distribuée à des tiers...&lt;/p&gt;

&lt;p&gt;Mais pour des migrations, du code framework, ou n’importe quel code distribué, il sera certainement votre meilleur ami, et surtout celui de vos consommateurs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Le dépôt
&lt;/h2&gt;

&lt;p&gt;Vous pouvez retrouver le code complet de cet article sur le dépôt &lt;a href="https://github.com/jtama/openrewrite-refactoring-as-code" rel="noopener noreferrer"&gt;Openrewrite: Refactoring as Code&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>tooling</category>
      <category>refactoring</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
