From d739aa19640a64bbf7b7c76525d1818a287224c4 Mon Sep 17 00:00:00 2001
From: rschoene <rene.schoene@tu-dresden.de>
Date: Fri, 26 Aug 2022 14:08:54 +0200
Subject: [PATCH] 0.2.5

- add option to generate separate grammar diagrams
- reverted to separate inheritance type to exclude them if necessary
- added some attributes to silence JastAdd warnings
---
 grammar2uml/build.gradle                      |  2 +-
 grammar2uml/src/main/jastadd/Analysis.jrag    | 62 +++++++++++++
 .../src/main/jastadd/Grammar2Uml.relast       |  2 +-
 .../src/main/jastadd/MustacheNodes.relast     |  9 +-
 grammar2uml/src/main/jastadd/Navigation.jrag  |  7 ++
 .../src/main/jastadd/backend/Generation.jadd  | 32 ++++++-
 .../grammar2uml/compiler/Compiler.java        | 17 +++-
 .../compiler/Grammar2UmlProcessor.java        | 89 +++++++++++++++----
 .../grammar2uml/compiler/SimpleMain.java      | 13 ++-
 .../src/main/resources/Containment.mustache   |  2 +
 .../src/main/resources/Inheritance.mustache   |  3 +
 .../src/main/resources/Relation.mustache      |  2 +
 .../src/main/resources/TypeDecl.mustache      |  7 +-
 .../src/main/resources/grammar2uml.mustache   |  3 +-
 .../resources/grammar2umlVersion.properties   |  4 +-
 15 files changed, 221 insertions(+), 33 deletions(-)
 create mode 100644 grammar2uml/src/main/resources/Inheritance.mustache

diff --git a/grammar2uml/build.gradle b/grammar2uml/build.gradle
index 288271b..5f4002d 100644
--- a/grammar2uml/build.gradle
+++ b/grammar2uml/build.gradle
@@ -161,7 +161,7 @@ jastadd {
     parser.genDir = "src/gen/java/de/tudresden/inf/st/jastadd/grammar2uml/parser"
 
     // default options are: '--rewrite=cnta', '--safeLazy', '--visitCheck=false', '--cacheCycle=false'
-    extraJastAddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--visitCheck=true"]
+    extraJastAddOptions = ["--lineColumnNumbers", "--List=JastAddList"]
 }
 
 // --- Tests ---
diff --git a/grammar2uml/src/main/jastadd/Analysis.jrag b/grammar2uml/src/main/jastadd/Analysis.jrag
index 317cf9d..47023ef 100644
--- a/grammar2uml/src/main/jastadd/Analysis.jrag
+++ b/grammar2uml/src/main/jastadd/Analysis.jrag
@@ -1,6 +1,7 @@
 aspect Analysis {
   syn lazy Folder GrammarFile.defaultFolder() {
     Folder result = new Folder();
+    result.setActive(true);
     result.setName(new java.io.File(getFileName()).getName().replace(".relast", "").replace(".ast", ""));
     for (Declaration decl : getDeclarationList()) {
       if (decl.isTypeDecl() && !decl.asTypeDecl().hasSourceFolder()) {
@@ -9,4 +10,65 @@ aspect Analysis {
     }
     return result;
   }
+
+  syn java.util.Set<TypeDecl> Grammar2Uml.activeTypeDecls() {
+    java.util.Set<TypeDecl> result = new java.util.HashSet<>();
+    for (Folder folder : getFolderList()) {
+      if (folder.getActive()) {
+        folder.getTypeList().forEach(typeDecl -> result.add(typeDecl));
+      }
+    }
+    return result;
+  }
+
+  syn boolean TypeDecl.relatesToActiveTypeDecl() {
+    java.util.Set<TypeDecl> activeTypeDecls = grammar2uml().activeTypeDecls();
+    // self
+    if (activeTypeDecls.contains(this)) {
+      return true;
+    }
+
+    // inheritance
+    if (hasSuperType() && activeTypeDecls.contains(getSuperType())) {
+      return true;
+    }
+    for (TypeDecl subType : getSubTypeList()) {
+      if (activeTypeDecls.contains(subType)) {
+        return true;
+      }
+    }
+
+    // relations
+    for (Role role : getRoleList()) {
+      Relation relation = role.containingRelation();
+      boolean activeRelation = relation.isDirectedRelation() ?
+        (activeTypeDecls.contains(relation.asDirectedRelation().getSource().getType()) ||
+         activeTypeDecls.contains(relation.asDirectedRelation().getTarget().getType())) :
+        (activeTypeDecls.contains(relation.asBidirectionalRelation().getLeft().getType()) ||
+         activeTypeDecls.contains(relation.asBidirectionalRelation().getRight().getType()));
+      if (activeRelation) {
+        return true;
+      }
+    }
+
+    // containment
+    for (TypeComponent potentialParent : getPotentialParentList()) {
+      if (activeTypeDecls.contains(potentialParent.containingTypeDecl())) {
+        return true;
+      }
+    }
+    for (Component component : getComponentList()) {
+      if (component.isTypeComponent() && activeTypeDecls.contains(component.asTypeComponent().getTypeDecl())) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  inh Relation Role.containingRelation();
+  eq DirectedRelation.getSource().containingRelation() = this;
+  eq DirectedRelation.getTarget().containingRelation() = this;
+  eq BidirectionalRelation.getLeft().containingRelation() = this;
+  eq BidirectionalRelation.getRight().containingRelation() = this;
 }
diff --git a/grammar2uml/src/main/jastadd/Grammar2Uml.relast b/grammar2uml/src/main/jastadd/Grammar2Uml.relast
index 1541b08..36a4456 100644
--- a/grammar2uml/src/main/jastadd/Grammar2Uml.relast
+++ b/grammar2uml/src/main/jastadd/Grammar2Uml.relast
@@ -1,6 +1,6 @@
 Grammar2Uml ::= Program <FileName> Folder* <StyleDefinition:StyleDefinition> ;
 
-Folder ::= <Name:String> ;
+Folder ::= <Name:String> <Active:boolean> ;
 rel Folder.Type* <-> TypeDecl.SourceFolder?;
 
 Style ::= <BackgroundColor> <InlineAsSuperType:boolean> ;
diff --git a/grammar2uml/src/main/jastadd/MustacheNodes.relast b/grammar2uml/src/main/jastadd/MustacheNodes.relast
index faf2ef8..1b65fc8 100644
--- a/grammar2uml/src/main/jastadd/MustacheNodes.relast
+++ b/grammar2uml/src/main/jastadd/MustacheNodes.relast
@@ -1,5 +1,5 @@
-MGrammar2Uml ::= Folder:MFolder* OtherType:MTypeDecl* Containment:MContainment* Relation:MRelation* ;
-MFolder ::= InnerTypeDecl:MTypeDecl*;
+MGrammar2Uml ::= Folder:MFolder* OtherType:MTypeDecl* Containment:MContainment* Relation:MRelation* Inheritance:MInheritance* ;
+MFolder ::= InnerTypeDecl:MTypeDecl* <Active:boolean> ;
 MTypeDecl ::= InnerTokenComponent:MTokenComponent* <Name> Style;
 MTokenComponent;
 abstract MContainment ::= <Label:String> ;
@@ -7,6 +7,7 @@ MSingleContainment : MContainment;
 MOptContainment : MContainment;
 MListContainment : MContainment;
 MRelation ::= <Label> <LeftModifier> <RightModifier> <Bidirectional:boolean>;
+MInheritance;
 
 rel MGrammar2Uml.Grammar2Uml -> Grammar2Uml;
 rel MFolder.Folder -> Folder;
@@ -16,4 +17,6 @@ rel MContainment.Type -> MTypeDecl;
 rel MContainment.Component -> MTypeDecl;
 rel MRelation.Left -> MTypeDecl;
 rel MRelation.Right -> MTypeDecl;
-rel MTypeDecl.SuperClass? -> MTypeDecl;
+//rel MTypeDecl.SuperClass? -> MTypeDecl;
+rel MInheritance.SuperClass -> MTypeDecl;
+rel MInheritance.SubClass -> MTypeDecl;
diff --git a/grammar2uml/src/main/jastadd/Navigation.jrag b/grammar2uml/src/main/jastadd/Navigation.jrag
index f519c0a..8c85611 100644
--- a/grammar2uml/src/main/jastadd/Navigation.jrag
+++ b/grammar2uml/src/main/jastadd/Navigation.jrag
@@ -3,16 +3,23 @@ aspect Navigation {
   // --- program ---
   eq Grammar2Uml.getChild().program() = getProgram();
   eq MGrammar2Uml.getChild().program() = getGrammar2Uml().program();
+  eq Document.getChild().program() = null;
 
   // --- grammar2uml ---
   inh Grammar2Uml ASTNode.grammar2uml();
   eq Grammar2Uml.getChild().grammar2uml() = this;
   eq MGrammar2Uml.getChild().grammar2uml() = getGrammar2Uml();
+  eq Document.getChild().grammar2uml() = null;
 
   // --- containedFile ---
   eq Grammar2Uml.getChild().containedFile() = null;
   eq MGrammar2Uml.getChild().containedFile() = null;
+  eq Document.getChild().containedFile() = null;
 
   // --- containedFileName ---
   eq Grammar2Uml.containedFileName() = getFileName();
+
+  // --- containingFolder ---
+  inh MFolder MTypeDecl.containingFolder();
+  eq MFolder.getInnerTypeDecl().containingFolder() = this;
 }
diff --git a/grammar2uml/src/main/jastadd/backend/Generation.jadd b/grammar2uml/src/main/jastadd/backend/Generation.jadd
index fca8e4a..39fe55d 100644
--- a/grammar2uml/src/main/jastadd/backend/Generation.jadd
+++ b/grammar2uml/src/main/jastadd/backend/Generation.jadd
@@ -27,6 +27,14 @@ aspect AttributesForMustache {
 
   syn boolean MTypeDecl.isAbstract() = getType().getAbstract();
 
+  syn boolean MTypeDecl.relatesToActiveTypeDecl() = getType().relatesToActiveTypeDecl();
+  syn boolean MTypeDecl.folderInactiveAndUnrelated() = !containingFolderActive() && !relatesToActiveTypeDecl();
+  syn boolean MTypeDecl.folderInactiveAndRelated() = !containingFolderActive() && relatesToActiveTypeDecl();
+
+  syn boolean MTypeDecl.containingFolderActive() {
+    return !getType().hasSourceFolder() || getType().getSourceFolder().getActive();
+  }
+
   syn String MTypeDecl.backgroundColor() = getStyle().getBackgroundColor();
 
   // --- MTokenComponent ---
@@ -51,12 +59,24 @@ aspect AttributesForMustache {
 //  eq MSingleRelation.modifier() = "\"1\"";
 //  eq MOptRelation.modifier() = "\"0 .. 1\"";
 //  eq MListRelation.modifier() = "\"*\"";
+  syn boolean MRelation.bothRelated() = getLeft().relatesToActiveTypeDecl() && getRight().relatesToActiveTypeDecl();
+  syn boolean MRelation.atLeastOneInActiveFolder() = getLeft().containingFolderActive() ||
+      getRight().containingFolderActive();
+
+  // --- MInheritance ---
+  syn boolean MInheritance.bothRelated() = getSuperClass().relatesToActiveTypeDecl() &&
+      getSubClass().relatesToActiveTypeDecl();
+  syn boolean MInheritance.atLeastOneInActiveFolder() = getSuperClass().containingFolderActive() ||
+      getSubClass().containingFolderActive();
 
   // --- toMContainment ---
   syn MContainment TypeComponent.toMContainment();
   eq NormalComponent.toMContainment() = new MSingleContainment();
   eq ListComponent.toMContainment() = new MListContainment();
   eq OptComponent.toMContainment() = new MOptContainment();
+  syn boolean MContainment.bothRelated() = getType().relatesToActiveTypeDecl() && getComponent().relatesToActiveTypeDecl();
+  syn boolean MContainment.atLeastOneInActiveFolder() = getType().containingFolderActive() ||
+      getComponent().containingFolderActive();
 
   // --- toMRelation ---
   syn MRelation Relation.toMRelation();
@@ -98,6 +118,15 @@ aspect AttributesForMustache {
       if (!typeDecl.hasSourceFolder()) {
         result.addOtherType(typeDecl.toMustache());
       }
+      if (typeDecl.hasSuperType()) {
+        Style parentStyle = typeDecl.getSuperType().toMustache().getStyle();
+        if (!parentStyle.getInlineAsSuperType()) {
+          MInheritance inheritance = new MInheritance();
+          inheritance.setSuperClass(typeDecl.getSuperType().toMustache());
+          inheritance.setSubClass(typeDecl.toMustache());
+          result.addInheritance(inheritance);
+        }
+      }
       for (Component component : typeDecl.getComponentList()) {
         if (component.isTypeComponent()) {
           TypeComponent typeComponent = component.asTypeComponent();
@@ -137,6 +166,7 @@ aspect AttributesForMustache {
     for (TypeDecl typeDecl : getTypeList()) {
       result.addInnerTypeDecl(typeDecl.toMustache());
     }
+    result.setActive(this.getActive());
     return result;
   }
 
@@ -157,8 +187,6 @@ aspect AttributesForMustache {
       Style parentStyle = this.getSuperType().toMustache().getStyle();
       if (parentStyle.getInlineAsSuperType()) {
         result.setName(getName() + ":" + this.getSuperType().getName());
-      } else {
-        result.setSuperClass(this.getSuperType().toMustache());
       }
     }
     return result;
diff --git a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java
index 41116fa..fa922d8 100644
--- a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java
+++ b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java
@@ -12,6 +12,7 @@ import org.jastadd.relast.compiler.CompilerException;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.MissingResourceException;
 import java.util.ResourceBundle;
@@ -21,6 +22,7 @@ public class Compiler extends AbstractCompiler {
   private final Grammar2UmlProcessor processor;
   private ValueOption optionOutputFile;
   private ValueOption optionInputGrammar2Uml;
+  private ValueOption optionGenerateSeparateGrammarDiagrams;
   private BooleanOption optionDefaultFolders;
   private BooleanOption optionHelp;
   private BooleanOption optionVersion;
@@ -69,6 +71,9 @@ public class Compiler extends AbstractCompiler {
     optionInputGrammar2Uml = addOption(
         new ValueOption("inputGrammar2Uml" , "grammar2uml definition file.")
             .needsValue(true));
+    optionGenerateSeparateGrammarDiagrams = addOption(
+        new ValueOption("separateGrammarDiagrams" , "Generate separate grammar diagrams according to the given pattern (e.g., 'gen/*.png'). Must contain a '*' for separation.")
+            .needsValue(true));
     optionDefaultFolders = addOption(
         new BooleanOption("defaultFolders" ,
             "Creates a default folder per grammar file.")
@@ -113,8 +118,16 @@ public class Compiler extends AbstractCompiler {
     }
 
     processor.setUseDefaultFolders(optionDefaultFolders.value())
-        .setVerbose(optionVerbose.value())
-        .writeFile(Paths.get(optionOutputFile.value()).toAbsolutePath());
+        .setVerbose(optionVerbose.value());
+
+    processor.writeFile(Paths.get(optionOutputFile.value()).toAbsolutePath());
+
+    if (optionGenerateSeparateGrammarDiagrams.isMatched()) {
+      Path destinationPattern = Path.of(optionGenerateSeparateGrammarDiagrams.value());
+      Path parent = destinationPattern.getParent();
+      String extension = Grammar2UmlProcessor.fileExtensionOf(destinationPattern.getFileName());
+      processor.generateSeparateGrammarDiagrams(parent, extension);
+    }
 
     return 0;
   }
diff --git a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Grammar2UmlProcessor.java b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Grammar2UmlProcessor.java
index 447214a..5e566eb 100644
--- a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Grammar2UmlProcessor.java
+++ b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Grammar2UmlProcessor.java
@@ -14,9 +14,7 @@ import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
 import java.util.function.Consumer;
 
 /**
@@ -29,7 +27,7 @@ public class Grammar2UmlProcessor {
   private boolean verbose;
   private boolean useDefaultFolders;
   private final List<Folder> folders = new ArrayList<>();
-  private String generatedAspect = null;
+  private Grammar2Uml grammar2uml = null;
   private final List<Consumer<Grammar2Uml>> callbacks = new ArrayList<>();
   private StyleDefinition styleDefinition = (typeDecl, style) -> {};
 
@@ -66,17 +64,22 @@ public class Grammar2UmlProcessor {
   public void writeFile(Path destination) throws CompilerException {
     build();
 
-    Path parent = destination.toAbsolutePath().getParent();
+    String generatedAspect = grammar2uml.generateAspect();
+
+    ensureDirectory(destination.toAbsolutePath().getParent());
+
+    writeFileContent(destination, generatedAspect);
+  }
+
+  private void ensureDirectory(Path directory) throws CompilerException {
     try {
-      Files.createDirectories(parent);
+      Files.createDirectories(directory);
     } catch (IOException e) {
-      throw new CompilerException("Error creating output dir " + parent, e);
-    }
-
-    if (inputGrammarFiles.isEmpty()) {
-      throw new CompilerException("No input grammars specified!");
+      throw new CompilerException("Error creating output dir " + directory, e);
     }
+  }
 
+  private void writeFileContent(Path destination, String generatedAspect) throws CompilerException {
     printMessage("Writing output file " + destination);
     String extension = fileExtensionOf(destination).toUpperCase();
     FileFormatOption plantUmlOption = null;
@@ -105,13 +108,69 @@ public class Grammar2UmlProcessor {
     }
   }
 
+  public void generateSeparateGrammarDiagrams(Path destinationDirectory, String extension) throws CompilerException {
+    build();
+
+    ensureDirectory(destinationDirectory);
+
+    // remember active states, and set them all to false
+    Set<Folder> previouslyActive = new HashSet<>();
+    for (Folder folder : grammar2uml.getFolderList()) {
+      if (folder.getActive()) {
+        previouslyActive.add(folder);
+      }
+      folder.setActive(false);
+    }
+
+    for (Folder folder : grammar2uml.getFolderList()) {
+      folder.setActive(true);
+
+      // generate aspect and write file
+      grammar2uml.flushTreeCache();
+
+      System.out.println("--- " + folder.getName() + " ---");
+
+      String generatedAspect = grammar2uml.generateAspect();
+      writeFileContent(destinationDirectory.resolve(folder.getName() + "." + extension), generatedAspect);
+
+      folder.setActive(false);
+    }
+
+    // restore previous active states
+    for (Folder folder : grammar2uml.getFolderList()) {
+      if (previouslyActive.contains(folder)) {
+        folder.setActive(true);
+      }
+    }
+  }
+
+//  private void debugAttributes() {
+//    for (Folder folder : grammar2uml.getFolderList()) {
+//      System.out.println(folder.getName() + ": active=" + folder.getActive() + ", mFolder.active=" + folder.toMustache().getActive());
+//    }
+//    for (TypeDecl typeDecl : grammar2uml.getProgram().typeDecls().stream()
+//        .sorted(Comparator.comparing(TypeDecl::getName))
+//        .collect(Collectors.toList())) {
+//      MTypeDecl mTypeDecl = typeDecl.toMustache();
+//      System.out.println(typeDecl.getName() + ": " + String.join(", ",
+//          "relates=" + typeDecl.relatesToActiveTypeDecl(),
+//          "folderInactive+Unrelated=" + mTypeDecl.folderInactiveAndUnrelated(),
+//          "folderInactive+Related=" + mTypeDecl.folderInactiveAndRelated(),
+//          "folderActive=" + mTypeDecl.containingFolderActive()));
+//    }
+//  }
+
   private void build() throws CompilerException {
-    if (generatedAspect != null) {
+    if (grammar2uml != null) {
       return;  // already built
     }
 
+    if (inputGrammarFiles.isEmpty()) {
+      throw new CompilerException("No input grammars specified!");
+    }
+
     Program program = new Program();
-    Grammar2Uml grammar2uml = new Grammar2Uml();
+    grammar2uml = new Grammar2Uml();
     for (String inputGrammarFileName : inputGrammarFiles) {
       printMessage("Parsing " + inputGrammarFileName);
       GrammarFile inputGrammar;
@@ -152,8 +211,6 @@ public class Grammar2UmlProcessor {
       }
       throw new CompilerException("Exiting because of previous errors.");
     }
-
-    generatedAspect = grammar2uml.generateAspect();
   }
 
   private void printMessage(String message) {
@@ -162,7 +219,7 @@ public class Grammar2UmlProcessor {
     }
   }
 
-  private String fileExtensionOf(Path path) {
+  static String fileExtensionOf(Path path) {
     String fileName = path.toFile().getName();
     int lastIndexOfDot = fileName.lastIndexOf(".");
     return fileName.substring(lastIndexOfDot + 1);
diff --git a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/SimpleMain.java b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/SimpleMain.java
index 443b263..71f9b7d 100644
--- a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/SimpleMain.java
+++ b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/SimpleMain.java
@@ -28,7 +28,11 @@ public class SimpleMain {
   private static void useAPI() throws CompilerException {
     Grammar2UmlProcessor processor = new Grammar2UmlProcessor();
     // run --args 'src/main/jastadd/Grammar2Uml.relast src/gen/jastadd-sources/relast.preprocessor/RelAst.relast --defaultFolders --output=uml.md'
-    processor.addGrammar("src/main/jastadd/Grammar2Uml.relast",
+    processor
+        .setVerbose(false)
+        .addFolder()
+        .addPostProcessing(grammar2Uml -> {})
+        .addGrammar("src/main/jastadd/Grammar2Uml.relast",
         "src/gen/jastadd-sources/relast.preprocessor/RelAst.relast")
         .setStyleDefinition((typeDecl, style) -> {
           switch (typeDecl.getName()) {
@@ -41,8 +45,11 @@ public class SimpleMain {
           }
         })
         .setUseDefaultFolders(true);
-    processor.writeFile(Paths.get("api.md"));
-    processor.writeFile(Paths.get("api.png"));
+    System.out.println("== API == ");
+//    processor.writeFile(Paths.get("api.md"));
+//    processor.writeFile(Paths.get("api.png"));
+    System.out.println("== SEPARATE == ");
+    processor.generateSeparateGrammarDiagrams(Paths.get("."), "png");
   }
 
   public static Grammar2Uml createManualAST() {
diff --git a/grammar2uml/src/main/resources/Containment.mustache b/grammar2uml/src/main/resources/Containment.mustache
index 58bc4e9..82b6351 100644
--- a/grammar2uml/src/main/resources/Containment.mustache
+++ b/grammar2uml/src/main/resources/Containment.mustache
@@ -1 +1,3 @@
+{{#atLeastOneInActiveFolder}}
 {{{typeName}}} *-- {{{modifier}}} {{{componentName}}} {{#Label}}: {{Label}}{{/Label}}
+{{/atLeastOneInActiveFolder}}
diff --git a/grammar2uml/src/main/resources/Inheritance.mustache b/grammar2uml/src/main/resources/Inheritance.mustache
new file mode 100644
index 0000000..c856091
--- /dev/null
+++ b/grammar2uml/src/main/resources/Inheritance.mustache
@@ -0,0 +1,3 @@
+{{#atLeastOneInActiveFolder}}
+{{#SuperClass}}"{{{Name}}}"{{/SuperClass}} <|-- {{#SubClass}}"{{{Name}}}"{{/SubClass}}
+{{/atLeastOneInActiveFolder}}
diff --git a/grammar2uml/src/main/resources/Relation.mustache b/grammar2uml/src/main/resources/Relation.mustache
index 6e9de53..14c6c97 100644
--- a/grammar2uml/src/main/resources/Relation.mustache
+++ b/grammar2uml/src/main/resources/Relation.mustache
@@ -1 +1,3 @@
+{{#atLeastOneInActiveFolder}}
 {{{leftName}}} {{{leftModifier}}} {{#isBidirectional}}<{{/isBidirectional}}-[norank]-> {{{rightModifier}}} {{{rightName}}} {{#Label}}: {{Label}}{{/Label}}
+{{/atLeastOneInActiveFolder}}
diff --git a/grammar2uml/src/main/resources/TypeDecl.mustache b/grammar2uml/src/main/resources/TypeDecl.mustache
index 4196fea..b402aae 100644
--- a/grammar2uml/src/main/resources/TypeDecl.mustache
+++ b/grammar2uml/src/main/resources/TypeDecl.mustache
@@ -1,6 +1,9 @@
-{{#isAbstract}}abstract {{/isAbstract}}class "{{{Name}}}"{{#backgroundColor}} #{{{backgroundColor}}}{{/backgroundColor}}{{#InnerTokenComponents}}{{#first}} {
+{{^folderInactiveAndUnrelated}}
+{{#isAbstract}}abstract {{/isAbstract}}class "{{{Name}}}"{{#backgroundColor}} #{{{backgroundColor}}}{{/backgroundColor}}{{#folderInactiveAndRelated}} ##[dashed]{{/folderInactiveAndRelated}}{{^folderInactiveAndRelated}}{{#InnerTokenComponents}}{{#first}} {
 {{/first}}
   {{{type}}} {{{name}}}
 {{#last}}
 }{{/last}}{{/InnerTokenComponents}}
-{{#hasSuperClass}}{{#SuperClass}}"{{{Name}}}"{{/SuperClass}} <|-- "{{{Name}}}"{{/hasSuperClass}}
+
+{{/folderInactiveAndRelated}}
+{{/folderInactiveAndUnrelated}}
diff --git a/grammar2uml/src/main/resources/grammar2uml.mustache b/grammar2uml/src/main/resources/grammar2uml.mustache
index e2b13e3..b71f011 100644
--- a/grammar2uml/src/main/resources/grammar2uml.mustache
+++ b/grammar2uml/src/main/resources/grammar2uml.mustache
@@ -1,10 +1,11 @@
 ```plantuml
 @startuml
 hide circle
-hide methods
+hide empty members
 {{#Folders}}{{> Folder}}{{/Folders}}
 {{#OtherTypes}}{{> TypeDecl}}{{/OtherTypes}}
 {{#Containments}}{{> Containment}}{{/Containments}}
 {{#Relations}}{{> Relation}}{{/Relations}}
+{{#Inheritances}}{{> Inheritance}}{{/Inheritances}}
 @enduml
 ```
diff --git a/grammar2uml/src/main/resources/grammar2umlVersion.properties b/grammar2uml/src/main/resources/grammar2umlVersion.properties
index 04f87fb..c1ab792 100644
--- a/grammar2uml/src/main/resources/grammar2umlVersion.properties
+++ b/grammar2uml/src/main/resources/grammar2umlVersion.properties
@@ -1,2 +1,2 @@
-#Sat May 21 15:41:29 CEST 2022
-version=0.2.4
+#Fri Aug 26 14:06:56 CEST 2022
+version=0.2.5
-- 
GitLab