content: import com.adacore.libfoolang.Libfoolang.*;
  
  import java.util.Arrays;
  
  public class RewritingTests {
  
      // ----- Utils -----
  
      /** Functional interface representing a simple block of code. */
      @FunctionalInterface
      public interface Executable {
          void execute();
      }
  
      /**
       * Display the section header with the given name
       *
       * @param name The name of the section
       */
      private static void header(String name) {
          System.out.println("--- " + name + " ---");
      }
  
      /**
       * Display the footer of a section
       *
       * @param name The name of the section
       */
      private static void footer(String name) {
          System.out.println("----" + "-".repeat(name.length()) + "----\n");
      }
  
      /**
       * Util function for assertions
       */
      private static void assertTrue(
          String what,
          boolean predicate
      ) throws RuntimeException {
          System.out.println("Asserting: " + what);
          if (!predicate) {
              throw new RuntimeException("Assertion \"" + what + "\" failed");
          }
      }
  
      private static void assertLangkitException(
          String what,
          Executable executable
      ) {
          try {
              System.out.println("Try: " + what);
              executable.execute();
              throw new RuntimeException("Assertion \"" + what + "\" failed");
          } catch (LangkitException e) {
              System.out.println("  Exception kind = " + e.kind);
              System.out.println("  Exception message = " + e.getMessage());
          }
      }
  
      // ----- Test methods -----
  
      /** Test the rewriting context creation and properties */
      private static void testRewritingContext() {
          header("Rewriting context");
          try (
              AnalysisContext context = AnalysisContext.create();
          ) {
              assertTrue(
                  "Rewriting context is None",
                  context.getRewritingContext() == RewritingContext.NONE
              );
  
              // Open a context and close it manually
              System.out.println("Creating rewriting context");
              RewritingContext rcontext1 = context.startRewriting();
              assertTrue("Rewriting context reference to analysis context",
                         rcontext1.getAnalysisContext() == context);
              assertLangkitException("Creating an other rewriting context",
                                     () -> context.startRewriting());
              assertTrue("Rewriting context is opened", !rcontext1.isClosed());
  
              System.out.println("Closing the rewriting context");
              rcontext1.close();
              assertTrue("Rewriting context is closed", rcontext1.isClosed());
  
              // Open a new context and apply it empty
              System.out.println("\nCreating an other rewriting context");
              RewritingContext rcontext2 = context.startRewriting();
              System.out.println("Applying the new rewriting context");
              RewritingApplyResult applyResult = rcontext2.apply();
              assertTrue("Rewriting result is successful", applyResult.success);
              assertTrue("Rewriting result has no diagnostics",
                         applyResult.getDiagnostics().length == 0);
              assertTrue("Rewriting result unit is none",
                         applyResult.unit == AnalysisUnit.NONE);
              assertTrue("Other rewriting context is closed",
                         rcontext2.isClosed());
  
              System.out.println("Free the apply result");
              applyResult.close();
          }
          footer("Rewriting context");
      }
  
      /** Test the rewriting unit creation and properties */
      private static void testRewritingUnit() {
          header("Rewriting unit");
          try (
              AnalysisContext context1 = AnalysisContext.create();
              AnalysisContext context2 = AnalysisContext.create();
              RewritingContext rcontext = context1.startRewriting();
          ) {
              // Create valid rewriting units
              System.out.println("Creating analysis and rewriting units");
              AnalysisUnit unit1 = context1.getUnitFromFile("s1.txt");
              AnalysisUnit unit2 = context1.getUnitFromFile("s2.txt");
              AnalysisUnit unit3 = context1.getUnitFromFile("s3.txt");
              AnalysisUnit nonRewritableUnit =
                  context2.getUnitFromFile("s1.txt");
              RewritingUnit runit1 = unit1.getRewritingUnit();
              RewritingUnit runit2 = unit2.getRewritingUnit();
              assertTrue("Analysis unit reference to rewriting unit",
                         unit1.getRewritingUnit().equals(runit1));
              assertTrue("Rewriting unit reference to analysis unit",
                         runit2.getAnalysisUnit().equals(unit2));
              System.out.println("s2.txt rewriting unit unparsing:");
              System.out.println("#####");
              System.out.println(runit2.unparse());
              System.out.println("#####");
              assertTrue("Fetched rewriting units count is 2",
                         rcontext.rewritingUnits().length == 2);
              assertLangkitException(
                  "Creating a rewriting unit from a non-rewritable context",
                  () -> nonRewritableUnit.getRewritingUnit()
              );
          }
          footer("Rewriting unit");
      }
  
      /** Test the rewriting node fetching and properties */
      private static void testRewritingNode() {
          header("Rewriting node");
          try (
              AnalysisContext context1 = AnalysisContext.create();
              AnalysisContext context2 = AnalysisContext.create();
              RewritingContext rcontext = context1.startRewriting();
          ) {
              // Create rewriting units
              System.out.println("Creating analysis and rewriting units");
              AnalysisUnit unit1 = context1.getUnitFromFile("s1.txt");
              AnalysisUnit unit2 = context2.getUnitFromFile("s2.txt");
              RewritingUnit runit1 = unit1.getRewritingUnit();
  
              // Get the roots and verify them
              System.out.println("\nGetting rewriting units root nodes");
              FooNode node1 = unit1.getRoot();
              FooNode node2 = unit2.getRoot();
              RewritingNode rnode1 = runit1.getRoot();
              assertTrue("Rewriting root is not None", !rnode1.isNone());
              assertTrue("Rewriting node parsed node reference",
                         node1.equals(rnode1.getParsedNode()));
              assertTrue("Parsed node rewriting reference",
                         rnode1.equals(node1.getRewritingNode()));
              assertTrue("Rewriting root kind is the same as root",
                         rnode1.getKind() == node1.getKind());
              assertTrue("Rewriting node context reference",
                         rcontext.equals(rnode1.getRewritingContext()));
              assertTrue("Rewriting root is tied", rnode1.isTied());
              assertLangkitException(
                  "Create a rewriting node from a non rewriting context",
                  () -> node2.getRewritingNode()
              );
              System.out.println("s1.txt rewriting root node image");
              System.out.println(rnode1.image());
              System.out.println("s1.txt rewriting root node unparsing");
              System.out.println("#####");
              System.out.println(rnode1.unparse());
              System.out.println("#####");
              assertTrue("Unit and root unparsing results are the same",
                         runit1.unparse().equals(rnode1.unparse()));
  
              // Root children visiting
              System.out.println("\nGetting the rewriting root children");
              RewritingNode[] children = rnode1.children();
              System.out.println(
                  "Root rewriting node children: " + Arrays.toString(children)
              );
              assertTrue("Children count is 3", children.length == 3);
              assertTrue("Child parent is the root",
                         children[0].parent().equals(rnode1));
  
              // Child navigation
              System.out.println("\nNavigating the root node children");
              RewritingNode rfirstChild = rnode1.firstChild();
              RewritingNode rlastChild = rnode1.lastChild();
              System.out.println("Root first child: " + rfirstChild);
              System.out.println("Root last child: " + rlastChild);
              assertTrue("Last child previous is the first child next",
                         rlastChild.previousChild()
                                   .equals(rfirstChild.nextChild()));
              assertTrue("Last child next is None",
                         rlastChild.nextChild().isNone());
              assertTrue("First child previous is None",
                         rfirstChild.previousChild().isNone());
  
              // First declaration visiting
              System.out.println(
                  "\nGetting the name and expression of the first declaration"
              );
              RewritingNode rdecl1 = children[0];
              RewritingNode rname1 = rdecl1.getChild(
                  MemberReference.FOO_DECL_F_NAME
              );
              RewritingNode rexpr1 = rdecl1.getChild(
                  MemberReference.FOO_DECL_F_EXPR
              );
              System.out.println("  name: " + rname1.toString());
              System.out.println("  expr: " + rexpr1.toString());
              assertLangkitException(
                  "Getting f_name child on the root list",
                  () -> rnode1.getChild(MemberReference.FOO_DECL_F_NAME)
              );
              assertLangkitException(
                  "Getting f_expr child on a name node",
                  () -> rname1.getChild(MemberReference.FOO_DECL_F_EXPR)
              );
              assertTrue("First child of the name is None",
                         rname1.firstChild().isNone());
              assertTrue("Last child of the expr is None",
                         rexpr1.lastChild().isNone());
          }
          footer("Rewriting node");
      }
  
      /** Text the rewriting node text manipulation methods */
      private static void testRewritingNodeTexts() {
          header("Rewriting node text getting/setting");
          try (
              AnalysisContext context = AnalysisContext.create();
              RewritingContext rcontext = context.startRewriting();
          ) {
              // Create rewriting units
              System.out.println("Creating analysis and rewriting units");
              AnalysisUnit unit = context.getUnitFromFile("s1.txt");
              RewritingUnit runit = unit.getRewritingUnit();
  
              // Get the first decl stmt and get/set the name text
              System.out.println("\nGetting the first declaration statement");
              RewritingNode rroot = runit.getRoot();
              RewritingNode rdecl1 = rroot.children()[0];
              System.out.println("Original declaration statement: '" +
                                 rdecl1.unparse().trim() +
                                 "'");
              RewritingNode rname1 = rdecl1.children()[0];
              assertTrue("Variable name is 'orig_1'",
                         rname1.getText().equals("orig_1"));
              System.out.println("Changing the variable name for 'hello'");
              rname1.setText("hello");
              assertTrue("Variable name has been changed",
                         rname1.getText().equals("hello"));
              System.out.println("New declaration statement: '" +
                                 rdecl1.unparse().trim() +
                                 "'");
              System.out.println("New rewriting unit:");
              System.out.println("#####");
              System.out.println(runit.unparse());
              System.out.println("#####");
          }
          footer("Rewriting node text getting/setting");
      }
  
      /** Test the rewriting node creation methods */
      private static void testRewritingNodeCreation() {
          header("Rewriting node creation");
          try (
              AnalysisContext context = AnalysisContext.create();
              RewritingContext rcontext = context.startRewriting();
          ) {
              // Create rewriting units
              System.out.println("Creating analysis and rewriting units");
              AnalysisUnit unit = context.getUnitFromFile("s1.txt");
              RewritingUnit runit = unit.getRewritingUnit();
              RewritingNode rroot = runit.getRoot();
  
              // Clone the first declaration statement
              System.out.println("\nCloning the first declaration statement");
              RewritingNode rdecl1 = rroot.firstChild();
              RewritingNode rdecl1Clone = rdecl1.clone();
              System.out.println("Source node: " + rdecl1);
              System.out.println("Cloned node: " + rdecl1Clone);
              assertTrue("Clone result is not tied", !rdecl1Clone.isTied());
              assertTrue("Clone is different from the source",
                         !rdecl1Clone.equals(rdecl1));
  
              // Create a declaration from scratch
              System.out.println(
                  "\nCreating a declaration statement from scratch"
              );
              RewritingNode rdeclCreated1 = rcontext.createNode(NodeKind.VAR);
              System.out.println("Created node: " + rdeclCreated1);
              assertTrue("Created node is not tied", !rdeclCreated1.isTied());
              assertTrue("Created node parsed node is None",
                         rdeclCreated1.getParsedNode().isNone());
              assertTrue("Created declaration name is None",
                         rdeclCreated1.getChild(MemberReference.FOO_DECL_F_NAME)
                                      .isNone());
  
              // Create a name and an integer literal from scratch
              System.out.println("\nCreating a name and an integer literal");
              RewritingNode rname1 =
                  rcontext.createTokenNode(NodeKind.NAME, "first_name");
              RewritingNode rlit1 =
                  rcontext.createTokenNode(NodeKind.LITERAL, "1");
              assertTrue("Create name is not tied", !rname1.isTied());
              assertTrue("Created name kind is NAME",
                         rname1.getKind() == NodeKind.NAME);
              assertTrue("Create name text is 'first_name'",
                         rname1.getText().equals("first_name"));
              assertLangkitException(
                  "Creating a token node with an non-token kind ",
                  () -> rcontext.createTokenNode(NodeKind.VAR, "impossible")
              );
  
              // Create another declaration directly with the name and value
              System.out.println(
                  "\nCreating a declaration statement with its children"
              );
              RewritingNode rdeclCreated2 =
                  rcontext.createNode(NodeKind.VAR, rname1, rlit1);
              System.out.println("Created node: " + rdeclCreated2);
              assertTrue("Created node is not tied", !rdeclCreated2.isTied());
              assertTrue("Created decl name is the previously created name",
                         rdeclCreated2.getChild(MemberReference.FOO_DECL_F_NAME)
                                      .equals(rname1));
              assertTrue(
                  "Created decl expression is the previously created one",
                  rdeclCreated2.getChild(MemberReference.FOO_DECL_F_EXPR)
                               .equals(rlit1)
              );
              assertLangkitException(
                  "Creating a node with already tied children",
                  () -> rcontext.createNode(NodeKind.VAR, rname1, rlit1)
              );
              RewritingNode rname2 =
                  rcontext.createTokenNode(NodeKind.NAME, "second_name");
              RewritingNode rlit2 =
                  rcontext.createTokenNode(NodeKind.LITERAL, "2");
              assertLangkitException(
                  "Creating a node with the invalid type",
                  () -> rcontext.createNode(NodeKind.NAME, rname2, rlit2)
              );
  
              // Create a declaration from a template
              System.out.println(
                  "\nCreating a declaration statement from a template"
              );
              RewritingNode rdeclCreated3 = rcontext.createFromTemplate(
                  "var {} = {}",
                  GrammarRule.VAR_RULE_RULE,
                  rname2,
                  rlit2
              );
              System.out.println("Created node: " + rdeclCreated3);
              assertTrue("Created node is a VAR",
                         rdeclCreated3.getKind() == NodeKind.VAR);
              assertTrue("Created node is not tied", !rdeclCreated3.isTied());
              assertTrue(
                  "Created declaration name text is 'second_name'",
                  rdeclCreated3.getChild(MemberReference.FOO_DECL_F_NAME)
                               .getText()
                               .equals("second_name")
              );
              assertTrue(
                  "Created declaration expression text is '2'",
                  rdeclCreated3.getChild(MemberReference.FOO_DECL_F_EXPR)
                               .getText()
                               .equals("2")
              );
              assertLangkitException(
                  "Creating from template with invalid argument count",
                  () -> rcontext.createFromTemplate(
                      "var {} = {}",
                      GrammarRule.VAR_RULE_RULE,
                      rname2
                  )
              );
  
              // Create the final declaration list with its children
              System.out.println("\nCreating the declaration list");
              RewritingNode rdeclList = rcontext.createNode(
                  NodeKind.DECL_LIST,
                  rdeclCreated2,
                  rdeclCreated3
              );
              System.out.println("Created declaration list: " + rdeclList);
              RewritingNode[] children = rdeclList.children();
              assertTrue("Created list child count is 2", children.length == 2);
              System.out.println("Create declaration list unparse result:");
              System.out.println("#####");
              System.out.println(rdeclList.unparse());
              System.out.println("#####");
          }
          footer("Rewriting node creation");
      }
  
      /**
       * Test the rewriting node modification operations (replace, add, remove)
       */
      private static void testRewritingNodeModifications() {
          header("Rewriting node modifications");
  
          try (
              AnalysisContext context = AnalysisContext.create();
              RewritingContext rcontext = context.startRewriting();
          ) {
              // Create rewriting units
              System.out.println("Creating analysis and rewriting units");
              AnalysisUnit unit = context.getUnitFromFile("s1.txt");
              RewritingUnit runit = unit.getRewritingUnit();
              RewritingNode rroot = runit.getRoot();
              RewritingNode[] children = rroot.children();
  
              // Set the first declaration name
              System.out.println("\nReplacing the first declaration name");
              RewritingNode rdecl1 = children[0];
              RewritingNode rname1 =
                  rdecl1.getChild(MemberReference.FOO_DECL_F_NAME);
              RewritingNode rnameCreated1 =
                  rcontext.createTokenNode(NodeKind.NAME, "replaced");
              assertTrue("First declaration name is 'orig_1'",
                         rname1.getText().equals("orig_1"));
              assertTrue("First declaration name is tied", rname1.isTied());
              rdecl1.setChild(MemberReference.FOO_DECL_F_NAME, rnameCreated1);
              RewritingNode rname2 =
                  rdecl1.getChild(MemberReference.FOO_DECL_F_NAME);
              assertTrue("Current name node is the created one",
                         rname2.equals(rnameCreated1));
              assertTrue("First declaration name is now 'replaced'",
                         rname2.getText().equals("replaced"));
              assertTrue("Created name is now tied", rnameCreated1.isTied());
              assertTrue("Old name node is not tied anymore", !rname1.isTied());
              assertLangkitException(
                  "Setting an already tied child",
                  () -> rdecl1.setChild(
                      MemberReference.FOO_DECL_F_NAME,
                      rnameCreated1
                  )
              );
  
              // Replace the first declaration name for the original one
              System.out.println(
                  "\nReplacing the first delaration name for the original one"
              );
              rname2.replace(rname1);
              assertTrue("Replacing name is now untied", !rname2.isTied());
              assertTrue("Original name is now tied", rname1.isTied());
              assertTrue(
                  "First declaration name is now 'orig_1'",
                  rdecl1.getChild(MemberReference.FOO_DECL_F_NAME)
                        .getText()
                        .equals("orig_1")
              );
              assertLangkitException(
                  "Replacing the name with an already tied node",
                  () -> rname1.replace(rname1)
              );
              assertLangkitException(
                  "Replacing an untied node",
                  () -> rname2.replace(rname2)
              );
  
              // Insert new declarations in the declaration list
              System.out.println("\nDeclaration list original unparse:");
              System.out.println("#####");
              System.out.println(rroot.unparse());
              System.out.println("#####");
  
              RewritingNode rdeclCreated1 = rcontext.createFromTemplate(
                  "var c1 = 1",
                  GrammarRule.VAR_RULE_RULE
              );
              RewritingNode rdeclCreated2 = rcontext.createFromTemplate(
                  "var c2 = 2",
                  GrammarRule.VAR_RULE_RULE
              );
              RewritingNode rdeclCreated3 = rcontext.createFromTemplate(
                  "var c3 = 3",
                  GrammarRule.VAR_RULE_RULE
              );
              RewritingNode rdeclCreated4 = rcontext.createFromTemplate(
                  "var c4 = 4",
                  GrammarRule.VAR_RULE_RULE
              );
  
              // Insert a new declaration at the beginning
              System.out.println("\nInserting a declaration at the beginning");
              rroot.insertFirst(rdeclCreated1);
              System.out.println("Declaration list unparse:");
              System.out.println("#####");
              System.out.println(rroot.unparse());
              System.out.println("#####");
              assertTrue("Inserted node is now tied", rdeclCreated1.isTied());
              assertTrue("First declaration is the inserted one",
                         rroot.firstChild().equals(rdeclCreated1));
              assertTrue("Children count is now 4",
                         rroot.children().length == 4);
              assertLangkitException(
                  "Inserting a first child in a non list node",
                  () -> rdeclCreated1.insertFirst(rname2)
              );
              assertLangkitException(
                  "Inserting an already tied child node",
                  () -> rroot.insertFirst(rdeclCreated1)
              );
  
              // Insert a new declaration at the end
              System.out.println("\nInserting a declartion at the end");
              rroot.insertLast(rdeclCreated2);
              System.out.println("Declaration list unparse:");
              System.out.println("#####");
              System.out.println(rroot.unparse());
              System.out.println("#####");
              assertTrue("Inserted node is now tied", rdeclCreated2.isTied());
              assertTrue("Last declaration is the inserted one",
                         rroot.lastChild().equals(rdeclCreated2));
              assertTrue("Children count is now 5",
                         rroot.children().length == 5);
              assertLangkitException(
                  "Inserting a last child in a non list node",
                  () -> rdeclCreated2.insertLast(rname2)
              );
              assertLangkitException(
                  "Inserting an already tied child node",
                  () -> rroot.insertLast(rdeclCreated2)
              );
  
              // Insert a node before the 3rd node
              System.out.println("\nInserting a declaration before the 3rd");
              RewritingNode rthird = rroot.children()[2];
              RewritingNode rthridName =
                  rthird.getChild(MemberReference.FOO_DECL_F_NAME);
              rthird.insertBefore(rdeclCreated3);
              System.out.println("Declaration list unparse:");
              System.out.println("#####");
              System.out.println(rroot.unparse());
              System.out.println("#####");
              assertTrue("Inserted node is now tied", rdeclCreated3.isTied());
              assertTrue("3rd declaration is the inserted one",
                         rroot.children()[2].equals(rdeclCreated3));
              assertTrue("Children count is now 6",
                         rroot.children().length == 6);
              assertLangkitException(
                  "Inserting a node before a non-list-parent node",
                  () -> rthridName.insertBefore(rname2)
              );
              assertLangkitException(
                  "Inserting an already tied node",
                  () -> rthird.insertBefore(rdeclCreated3)
              );
  
              // Insert a node after the 4th one
              System.out.println("\nInserting a declaration after the 4th");
              rthird.insertAfter(rdeclCreated4);
              System.out.println("Declaration list unparse:");
              System.out.println("#####");
              System.out.println(rroot.unparse());
              System.out.println("#####");
              assertTrue("Inserted node is now tied", rdeclCreated4.isTied());
              assertTrue("5th declaration is the inserted one",
                         rroot.children()[4].equals(rdeclCreated4));
              assertTrue("Children count is now 7",
                         rroot.children().length == 7);
              assertLangkitException(
                  "Inserting a node before a non-list-parent node",
                  () -> rthridName.insertAfter(rname2)
              );
              assertLangkitException(
                  "Inserting an already tied node",
                  () -> rthird.insertAfter(rdeclCreated4)
              );
  
              // Remove the 2nd child from the declaration list
              System.out.println("\nRemoving the 2nd child");
              rroot.children()[1].removeFromParent();
              System.out.println("Declaration list unparse:");
              System.out.println("#####");
              System.out.println(rroot.unparse());
              System.out.println("#####");
              assertTrue("Children count is now 6",
                         rroot.children().length == 6);
              assertLangkitException(
                  "Removing a node which is not in a list node",
                  () -> rname1.removeFromParent()
              );
              assertLangkitException(
                  "Removing a node which is not tied",
                  () -> rname2.removeFromParent()
              );
          }
  
          footer("Rewriting node modifications");
      }
  
      /** Run all Java rewriting tests */
      public static void main(String[] args) {
          System.out.println("===== Start the Java rewriting tests =====");
          testRewritingContext();
          testRewritingUnit();
          testRewritingNode();
          testRewritingNodeTexts();
          testRewritingNodeCreation();
          testRewritingNodeModifications();
          System.out.println("===== End of the Java rewriting tests =====");
      }
  }
  