From ae54ec88cef57f9584eeccade8fe18c7604ddf8c Mon Sep 17 00:00:00 2001 From: rschoene <rene.schoene@tu-dresden.de> Date: Fri, 26 Aug 2022 09:49:50 +0200 Subject: [PATCH] API + some enhancements - added programmable interface (Grammar2UmlProcessor) - add types to tokens - API: add styling for TypeDecl - API: add option to inline superclass --- grammar2uml/build.gradle | 7 + grammar2uml/src/main/jastadd/Grammar2Uml.jadd | 5 + .../src/main/jastadd/Grammar2Uml.relast | 4 +- .../src/main/jastadd/MustacheNodes.relast | 16 +- .../jastadd/backend/AspectGeneration.jrag | 50 +++++ .../src/main/jastadd/backend/Generation.jadd | 102 +++-------- .../grammar2uml/compiler/Compiler.java | 133 ++------------ .../compiler/Grammar2UmlProcessor.java | 171 ++++++++++++++++++ .../{Grammar2UmlMain.java => SimpleMain.java} | 28 ++- .../src/main/resources/Containment.mustache | 2 +- .../src/main/resources/Inheritance.mustache | 1 - .../src/main/resources/Relation.mustache | 2 +- .../src/main/resources/TypeDecl.mustache | 11 +- .../src/main/resources/grammar2uml.mustache | 1 - 14 files changed, 323 insertions(+), 210 deletions(-) create mode 100644 grammar2uml/src/main/jastadd/Grammar2Uml.jadd create mode 100644 grammar2uml/src/main/jastadd/backend/AspectGeneration.jrag create mode 100644 grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Grammar2UmlProcessor.java rename grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/{Grammar2UmlMain.java => SimpleMain.java} (66%) delete mode 100644 grammar2uml/src/main/resources/Inheritance.mustache diff --git a/grammar2uml/build.gradle b/grammar2uml/build.gradle index 63f1b71..288271b 100644 --- a/grammar2uml/build.gradle +++ b/grammar2uml/build.gradle @@ -174,6 +174,13 @@ ext { application.mainClassName = "${mainClassName}" +task simpleRun(type: JavaExec) { + group 'application' + classpath sourceSets.main.runtimeClasspath + main = "de.tudresden.inf.st.jastadd.grammar2uml.compiler.SimpleMain" + +} + jar { manifest.attributes "Main-Class": "${mainClassName}" } diff --git a/grammar2uml/src/main/jastadd/Grammar2Uml.jadd b/grammar2uml/src/main/jastadd/Grammar2Uml.jadd new file mode 100644 index 0000000..8e76c85 --- /dev/null +++ b/grammar2uml/src/main/jastadd/Grammar2Uml.jadd @@ -0,0 +1,5 @@ +aspect GrammarTypes { + public interface StyleDefinition extends java.util.function.BiConsumer<TypeDecl, Style> { + + } +} diff --git a/grammar2uml/src/main/jastadd/Grammar2Uml.relast b/grammar2uml/src/main/jastadd/Grammar2Uml.relast index 9eab123..1541b08 100644 --- a/grammar2uml/src/main/jastadd/Grammar2Uml.relast +++ b/grammar2uml/src/main/jastadd/Grammar2Uml.relast @@ -1,4 +1,6 @@ -Grammar2Uml ::= Program <FileName> Folder* ; +Grammar2Uml ::= Program <FileName> Folder* <StyleDefinition:StyleDefinition> ; Folder ::= <Name:String> ; 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 d4b684c..faf2ef8 100644 --- a/grammar2uml/src/main/jastadd/MustacheNodes.relast +++ b/grammar2uml/src/main/jastadd/MustacheNodes.relast @@ -1,21 +1,19 @@ -MGrammar2Uml ::= Folder:MFolder* OtherType:MTypeDecl* Containment:MContainment* Relation:MRelation* Inheritance:MInheritance* ; +MGrammar2Uml ::= Folder:MFolder* OtherType:MTypeDecl* Containment:MContainment* Relation:MRelation* ; MFolder ::= InnerTypeDecl:MTypeDecl*; -MTypeDecl ::= InnerTokenComponent:MTokenComponent*; +MTypeDecl ::= InnerTokenComponent:MTokenComponent* <Name> Style; MTokenComponent; abstract MContainment ::= <Label:String> ; MSingleContainment : MContainment; MOptContainment : MContainment; MListContainment : MContainment; MRelation ::= <Label> <LeftModifier> <RightModifier> <Bidirectional:boolean>; -MInheritance ; rel MGrammar2Uml.Grammar2Uml -> Grammar2Uml; rel MFolder.Folder -> Folder; rel MTypeDecl.Type -> TypeDecl; rel MTokenComponent.Token -> TokenComponent; -rel MContainment.Type -> TypeDecl; -rel MContainment.Component -> TypeDecl; -rel MRelation.Left -> TypeDecl; -rel MRelation.Right -> TypeDecl; -rel MInheritance.SuperClass -> TypeDecl; -rel MInheritance.SubClass -> TypeDecl; +rel MContainment.Type -> MTypeDecl; +rel MContainment.Component -> MTypeDecl; +rel MRelation.Left -> MTypeDecl; +rel MRelation.Right -> MTypeDecl; +rel MTypeDecl.SuperClass? -> MTypeDecl; diff --git a/grammar2uml/src/main/jastadd/backend/AspectGeneration.jrag b/grammar2uml/src/main/jastadd/backend/AspectGeneration.jrag new file mode 100644 index 0000000..2faa596 --- /dev/null +++ b/grammar2uml/src/main/jastadd/backend/AspectGeneration.jrag @@ -0,0 +1,50 @@ +aspect AspectGeneration { + syn String Grammar2Uml.generateAspect() = toMustache().generateAspect(); + + syn String MGrammar2Uml.generateAspect() { + StringBuilder sb = new StringBuilder(); + com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() { + @Override + public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) { + return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) { + @Override + protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) { + com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes); + if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) { + throw new com.github.mustachejava.MustacheException(name + " not found in " + tc); + } + return wrapper; + } + }; + } + }; + com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory(); +// mf.setObjectHandler(roh); + com.github.mustachejava.Mustache m = mf.compile("grammar2uml.mustache"); + m.execute(new java.io.PrintWriter(new AppendableWriter(sb)), this); + return sb.toString(); + } + public class AppendableWriter extends java.io.Writer { + private final StringBuilder sb; + + public AppendableWriter(StringBuilder sb) { + this.sb = sb; + } + + @Override + public void write(char[] chars, int off, int len) { + sb.append(chars, off, len); + } + + @Override + public void write(String str) { + sb.append(str); + } + + @Override + public void flush() {} + + @Override + public void close() {} + } +} diff --git a/grammar2uml/src/main/jastadd/backend/Generation.jadd b/grammar2uml/src/main/jastadd/backend/Generation.jadd index ed5a3bb..fca8e4a 100644 --- a/grammar2uml/src/main/jastadd/backend/Generation.jadd +++ b/grammar2uml/src/main/jastadd/backend/Generation.jadd @@ -26,34 +26,32 @@ aspect AttributesForMustache { eq MTypeDecl.getInnerTokenComponent(int i).isLast() = i == getNumInnerTokenComponent() - 1; syn boolean MTypeDecl.isAbstract() = getType().getAbstract(); - syn String MTypeDecl.name() = getType().getName(); + + syn String MTypeDecl.backgroundColor() = getStyle().getBackgroundColor(); // --- MTokenComponent --- syn String MTokenComponent.name() = getToken().getName(); + syn String MTokenComponent.type() = getToken().hasJavaTypeUse() ? getToken().getJavaTypeUse().getName() : "String"; inh boolean MTokenComponent.isFirst(); inh boolean MTokenComponent.isLast(); // --- MContainment --- - syn String MContainment.typeName() = getType().getName(); - syn String MContainment.componentName() = getComponent().getName(); + syn String MContainment.typeName() = "\"" + getType().getName() + "\""; + syn String MContainment.componentName() = "\"" + getComponent().getName() + "\""; syn String MContainment.modifier(); eq MSingleContainment.modifier() = "\"1\""; eq MOptContainment.modifier() = "\"0 .. 1\""; eq MListContainment.modifier() = "\"*\""; // --- MRelation --- - syn String MRelation.leftName() = getLeft().getName(); - syn String MRelation.rightName() = getRight().getName(); + syn String MRelation.leftName() = "\"" + getLeft().getName() + "\""; + syn String MRelation.rightName() = "\"" + getRight().getName() + "\""; syn boolean MRelation.isBidirectional() = getBidirectional(); // syn String MRelation.modifier(); // eq MSingleRelation.modifier() = "\"1\""; // eq MOptRelation.modifier() = "\"0 .. 1\""; // eq MListRelation.modifier() = "\"*\""; - // --- MInheritance --- - syn String MInheritance.superClassName() = getSuperClass().getName(); - syn String MInheritance.subClassName() = getSubClass().getName(); - // --- toMContainment --- syn MContainment TypeComponent.toMContainment(); eq NormalComponent.toMContainment() = new MSingleContainment(); @@ -85,6 +83,10 @@ aspect AttributesForMustache { throw new RuntimeException("UnnamedRole cannot be converted to MRelation"); } + private Style TypeDecl.createDefaultStyle() { + return new Style().setBackgroundColor("white"); + } + // --- toMustache --- syn lazy MGrammar2Uml Grammar2Uml.toMustache() { MGrammar2Uml result = new MGrammar2Uml(); @@ -100,34 +102,28 @@ aspect AttributesForMustache { if (component.isTypeComponent()) { TypeComponent typeComponent = component.asTypeComponent(); MContainment containment = typeComponent.toMContainment(); - containment.setType(typeDecl); - containment.setComponent(component.asTypeComponent().getTypeDecl()); + containment.setType(typeDecl.toMustache()); + containment.setComponent(component.asTypeComponent().getTypeDecl().toMustache()); if (!component.getName().isEmpty() && !component.getName().equals(component.asTypeComponent().getTypeDecl().getName())) { containment.setLabel(component.getName()); } result.addContainment(containment); } } - if (typeDecl.hasSuperType()) { - MInheritance inheritance = new MInheritance(); - inheritance.setSuperClass(typeDecl.getSuperType()); - inheritance.setSubClass(typeDecl); - result.addInheritance(inheritance); - } } for (Relation relation : getProgram().relations()) { if (relation.isDirectedRelation()) { DirectedRelation directedRelation = relation.asDirectedRelation(); MRelation mRelation = directedRelation.toMRelation(); - mRelation.setLeft(directedRelation.getSource().getType()); - mRelation.setRight(directedRelation.getTarget().getType()); + mRelation.setLeft(directedRelation.getSource().getType().toMustache()); + mRelation.setRight(directedRelation.getTarget().getType().toMustache()); mRelation.setLabel(directedRelation.getSource().getName()); result.addRelation(mRelation); } else { BidirectionalRelation bidiRelation = relation.asBidirectionalRelation(); MRelation mRelation = bidiRelation.toMRelation(); - mRelation.setLeft(bidiRelation.getLeft().getType()); - mRelation.setRight(bidiRelation.getRight().getType()); + mRelation.setLeft(bidiRelation.getLeft().getType().toMustache()); + mRelation.setRight(bidiRelation.getRight().getType().toMustache()); // mRelation.setLabel(bidiRelation.getSource().getName()); result.addRelation(mRelation); } @@ -152,6 +148,19 @@ aspect AttributesForMustache { result.addInnerTokenComponent(component.asTokenComponent().toMustache()); } } + result.setName(this.getName()); + + Style style = createDefaultStyle(); + grammar2uml().getStyleDefinition().accept(this, style); + result.setStyle(style); + if (this.hasSuperType()) { + Style parentStyle = this.getSuperType().toMustache().getStyle(); + if (parentStyle.getInlineAsSuperType()) { + result.setName(getName() + ":" + this.getSuperType().getName()); + } else { + result.setSuperClass(this.getSuperType().toMustache()); + } + } return result; } @@ -162,54 +171,3 @@ aspect AttributesForMustache { } } - -aspect AspectGeneration { - syn String Grammar2Uml.generateAspect() = toMustache().generateAspect(); - - syn String MGrammar2Uml.generateAspect() { - StringBuilder sb = new StringBuilder(); - com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() { - @Override - public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) { - return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) { - @Override - protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) { - com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes); - if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) { - throw new com.github.mustachejava.MustacheException(name + " not found in " + tc); - } - return wrapper; - } - }; - } - }; - com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory(); -// mf.setObjectHandler(roh); - com.github.mustachejava.Mustache m = mf.compile("grammar2uml.mustache"); - m.execute(new java.io.PrintWriter(new AppendableWriter(sb)), this); - return sb.toString(); - } - public class AppendableWriter extends java.io.Writer { - private final StringBuilder sb; - - public AppendableWriter(StringBuilder sb) { - this.sb = sb; - } - - @Override - public void write(char[] chars, int off, int len) { - sb.append(chars, off, len); - } - - @Override - public void write(String str) { - sb.append(str); - } - - @Override - public void flush() {} - - @Override - public void close() {} - } -} 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 1b6bd40..41116fa 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 @@ -1,16 +1,9 @@ package de.tudresden.inf.st.jastadd.grammar2uml.compiler; import beaver.Parser; -import de.tudresden.inf.st.jastadd.grammar2uml.ast.ErrorMessage; import de.tudresden.inf.st.jastadd.grammar2uml.ast.Grammar2Uml; -import de.tudresden.inf.st.jastadd.grammar2uml.ast.GrammarFile; -import de.tudresden.inf.st.jastadd.grammar2uml.ast.Program; import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser; import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner; -import net.sourceforge.plantuml.FileFormat; -import net.sourceforge.plantuml.FileFormatOption; -import net.sourceforge.plantuml.FileUtils; -import net.sourceforge.plantuml.SourceStringReader; import org.jastadd.option.BooleanOption; import org.jastadd.option.ValueOption; import org.jastadd.relast.compiler.AbstractCompiler; @@ -19,13 +12,13 @@ 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; public class Compiler extends AbstractCompiler { + private final Grammar2UmlProcessor processor; private ValueOption optionOutputFile; private ValueOption optionInputGrammar2Uml; private BooleanOption optionDefaultFolders; @@ -35,6 +28,7 @@ public class Compiler extends AbstractCompiler { public Compiler() { super("grammar2uml" , false); + processor = new Grammar2UmlProcessor(); } /** @@ -66,10 +60,6 @@ public class Compiler extends AbstractCompiler { } } - private void printMessage(String message) { - System.out.println(message); - } - protected void initOptions() { optionOutputFile = addOption( new ValueOption("output" , "target file to be generated.") @@ -94,52 +84,6 @@ public class Compiler extends AbstractCompiler { .defaultValue(false)); } - private Grammar2Uml parseProgram() throws CompilerException { - Program program = new Program(); - Grammar2Uml grammar2Uml; - - for (String inputGrammarFileName : getConfiguration().getFiles()) { - printMessage("Parsing " + inputGrammarFileName); - GrammarFile inputGrammar; - try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputGrammarFileName))) { - Grammar2UmlScanner scanner = new Grammar2UmlScanner(reader); - Grammar2UmlParser parser = new Grammar2UmlParser(); - inputGrammar = (GrammarFile) parser.parse(scanner); - if (optionVerbose.value()) { - inputGrammar.dumpTree(System.out); - } - program.addGrammarFile(inputGrammar); - inputGrammar.setFileName(inputGrammarFileName); - } catch (IOException | Parser.Exception e) { - throw new CompilerException("Could not parse grammar file " + inputGrammarFileName, e); - } - } - - if (optionInputGrammar2Uml.isMatched()) { - String inputGrammar2UmlFileName = optionInputGrammar2Uml.value(); - try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputGrammar2UmlFileName))) { - Grammar2UmlScanner scanner = new Grammar2UmlScanner(reader); - Grammar2UmlParser parser = new Grammar2UmlParser(); - grammar2Uml = (Grammar2Uml) parser.parse(scanner, Grammar2UmlParser.AltGoals.grammar2uml); - grammar2Uml.setFileName(inputGrammar2UmlFileName); - } catch (IOException | Parser.Exception e) { - throw new CompilerException("Could not parse grammar2uml file " + inputGrammar2UmlFileName, e); - } - } else { - // no special setting given - grammar2Uml = new Grammar2Uml(); - grammar2Uml.setFileName("<none>"); - } - grammar2Uml.setProgram(program); - grammar2Uml.treeResolveAll(); - if (optionDefaultFolders.value()) { - for (GrammarFile grammarFile : program.getGrammarFileList()) { - grammar2Uml.addFolder(grammarFile.defaultFolder()); - } - } - return grammar2Uml; - } - @Override protected int compile() throws CompilerException { if (optionVersion.value()) { @@ -151,71 +95,28 @@ public class Compiler extends AbstractCompiler { return 0; } - printMessage("Running grammar2uml " + readVersion()); - - Path destination = getDestinationPath(); - Path parent = Paths.get(optionOutputFile.value()).toAbsolutePath().getParent(); - try { - Files.createDirectories(parent); - } catch (IOException e) { - throw new CompilerException("Error creating output dir " + parent, e); - } - - if (getConfiguration().getFiles().isEmpty()) { - throw new CompilerException("No input grammars specified!"); - } + System.out.println("Running grammar2uml " + readVersion()); - Grammar2Uml grammar2uml = parseProgram(); + getConfiguration().getFiles().forEach(processor::addGrammar); - if (!grammar2uml.errors().isEmpty()) { - System.err.println("Errors:"); - for (ErrorMessage e : grammar2uml.errors()) { - System.err.println(e); + if (optionInputGrammar2Uml.isMatched()) { + String inputGrammar2UmlFileName = optionInputGrammar2Uml.value(); + try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputGrammar2UmlFileName))) { + Grammar2UmlScanner scanner = new Grammar2UmlScanner(reader); + Grammar2UmlParser parser = new Grammar2UmlParser(); + Grammar2Uml grammar2Uml = (Grammar2Uml) parser.parse(scanner, Grammar2UmlParser.AltGoals.grammar2uml); + processor.addPostProcessing(g -> g.setFileName(inputGrammar2UmlFileName)); + grammar2Uml.getFolderList().forEach(processor::addFolder); + } catch (IOException | Parser.Exception e) { + throw new CompilerException("Could not parse grammar2uml file " + inputGrammar2UmlFileName, e); } - System.exit(1); } - printMessage("Writing output file " + destination); - - String sourceCode = grammar2uml.generateAspect(); - - // - String extension = fileExtensionOf(destination).toUpperCase(); - FileFormatOption plantUmlOption = null; - switch (extension) { - case "MD": - try { - Files.writeString(destination, sourceCode); - } catch (Exception e) { - throw new CompilerException("Could not write to file " + destination, e); - } - break; - case "HTML": - case "PNG": - case "PDF": - case "SVG": - plantUmlOption = new FileFormatOption(FileFormat.valueOf(extension)); - break; - } - if (plantUmlOption != null) { - try { - SourceStringReader reader = new SourceStringReader(sourceCode); - reader.outputImage(Files.newOutputStream(destination), plantUmlOption); - } catch (Exception e) { - throw new CompilerException("Could not write to file " + destination, e); - } - } + processor.setUseDefaultFolders(optionDefaultFolders.value()) + .setVerbose(optionVerbose.value()) + .writeFile(Paths.get(optionOutputFile.value()).toAbsolutePath()); return 0; } - private String fileExtensionOf(Path path) { - String fileName = path.toFile().getName(); - int lastIndexOfDot = fileName.lastIndexOf("."); - return fileName.substring(lastIndexOfDot + 1); - } - - private Path getDestinationPath() { - return Paths.get(optionOutputFile.value()).toAbsolutePath(); - } } 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 new file mode 100644 index 0000000..447214a --- /dev/null +++ b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Grammar2UmlProcessor.java @@ -0,0 +1,171 @@ +package de.tudresden.inf.st.jastadd.grammar2uml.compiler; + +import beaver.Parser; +import de.tudresden.inf.st.jastadd.grammar2uml.ast.*; +import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser; +import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner; +import net.sourceforge.plantuml.FileFormat; +import net.sourceforge.plantuml.FileFormatOption; +import net.sourceforge.plantuml.SourceStringReader; +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.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + +/** + * Producing images from grammars. + * + * @author rschoene - Initial contribution + */ +public class Grammar2UmlProcessor { + private final List<String> inputGrammarFiles = new ArrayList<>(); + private boolean verbose; + private boolean useDefaultFolders; + private final List<Folder> folders = new ArrayList<>(); + private String generatedAspect = null; + private final List<Consumer<Grammar2Uml>> callbacks = new ArrayList<>(); + private StyleDefinition styleDefinition = (typeDecl, style) -> {}; + + public Grammar2UmlProcessor addGrammar(String... inputGrammarFiles) { + Collections.addAll(this.inputGrammarFiles, inputGrammarFiles); + return this; + } + + public Grammar2UmlProcessor setVerbose(boolean value) { + this.verbose = value; + return this; + } + + public Grammar2UmlProcessor addFolder(Folder... folders) { + Collections.addAll(this.folders, folders); + return this; + } + + public Grammar2UmlProcessor setUseDefaultFolders(boolean value) { + useDefaultFolders = value; + return this; + } + + public Grammar2UmlProcessor addPostProcessing(Consumer<Grammar2Uml> callback) { + this.callbacks.add(callback); + return this; + } + + public Grammar2UmlProcessor setStyleDefinition(StyleDefinition definition) { + this.styleDefinition = definition; + return this; + } + + public void writeFile(Path destination) throws CompilerException { + build(); + + Path parent = destination.toAbsolutePath().getParent(); + try { + Files.createDirectories(parent); + } catch (IOException e) { + throw new CompilerException("Error creating output dir " + parent, e); + } + + if (inputGrammarFiles.isEmpty()) { + throw new CompilerException("No input grammars specified!"); + } + + printMessage("Writing output file " + destination); + String extension = fileExtensionOf(destination).toUpperCase(); + FileFormatOption plantUmlOption = null; + switch (extension) { + case "MD": + try { + Files.writeString(destination, generatedAspect); + } catch (Exception e) { + throw new CompilerException("Could not write to file " + destination, e); + } + break; + case "HTML": + case "PNG": + case "PDF": + case "SVG": + plantUmlOption = new FileFormatOption(FileFormat.valueOf(extension)); + break; + } + if (plantUmlOption != null) { + try { + SourceStringReader reader = new SourceStringReader(generatedAspect); + reader.outputImage(Files.newOutputStream(destination), plantUmlOption); + } catch (Exception e) { + throw new CompilerException("Could not write to file " + destination, e); + } + } + } + + private void build() throws CompilerException { + if (generatedAspect != null) { + return; // already built + } + + Program program = new Program(); + Grammar2Uml grammar2uml = new Grammar2Uml(); + for (String inputGrammarFileName : inputGrammarFiles) { + printMessage("Parsing " + inputGrammarFileName); + GrammarFile inputGrammar; + try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputGrammarFileName))) { + Grammar2UmlScanner scanner = new Grammar2UmlScanner(reader); + Grammar2UmlParser parser = new Grammar2UmlParser(); + inputGrammar = (GrammarFile) parser.parse(scanner); + if (verbose) { + inputGrammar.dumpTree(System.out); + } + program.addGrammarFile(inputGrammar); + inputGrammar.setFileName(inputGrammarFileName); + } catch (IOException | Parser.Exception e) { + throw new CompilerException("Could not parse grammar file " + inputGrammarFileName, e); + } + } + + if (useDefaultFolders) { + for (GrammarFile grammarFile : program.getGrammarFileList()) { + grammar2uml.addFolder(grammarFile.defaultFolder()); + } + } + + for (Folder folder : folders) { + grammar2uml.addFolder(folder); + } + + grammar2uml.setStyleDefinition(styleDefinition); + callbacks.forEach(consumer -> consumer.accept(grammar2uml)); + + grammar2uml.setProgram(program); + grammar2uml.treeResolveAll(); + + if (!grammar2uml.errors().isEmpty()) { + System.err.println("Errors:"); + for (ErrorMessage e : grammar2uml.errors()) { + System.err.println(e); + } + throw new CompilerException("Exiting because of previous errors."); + } + + generatedAspect = grammar2uml.generateAspect(); + } + + private void printMessage(String message) { + if (verbose) { + System.out.println(message); + } + } + + private 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/Grammar2UmlMain.java b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/SimpleMain.java similarity index 66% rename from grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Grammar2UmlMain.java rename to grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/SimpleMain.java index 17b105c..443b263 100644 --- a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Grammar2UmlMain.java +++ b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/SimpleMain.java @@ -4,6 +4,7 @@ import beaver.Parser; import de.tudresden.inf.st.jastadd.grammar2uml.ast.*; import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser; import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner; +import org.jastadd.relast.compiler.CompilerException; import java.io.BufferedReader; import java.io.IOException; @@ -16,11 +17,32 @@ import java.nio.file.Paths; * * @author rschoene - Initial contribution */ -public class Grammar2UmlMain { +public class SimpleMain { - public static void main(String[] args) { + public static void main(String[] args) throws Exception { // testing(); - processManualAST(); +// processManualAST(); + useAPI(); + } + + 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", + "src/gen/jastadd-sources/relast.preprocessor/RelAst.relast") + .setStyleDefinition((typeDecl, style) -> { + switch (typeDecl.getName()) { + case "Component": + case "Comment": + style.setInlineAsSuperType(true); + break; + case "TypeDecl": + style.setBackgroundColor("blue\\9932CC"); + } + }) + .setUseDefaultFolders(true); + processor.writeFile(Paths.get("api.md")); + processor.writeFile(Paths.get("api.png")); } public static Grammar2Uml createManualAST() { diff --git a/grammar2uml/src/main/resources/Containment.mustache b/grammar2uml/src/main/resources/Containment.mustache index 48dbc88..58bc4e9 100644 --- a/grammar2uml/src/main/resources/Containment.mustache +++ b/grammar2uml/src/main/resources/Containment.mustache @@ -1 +1 @@ -{{typeName}} *-- {{{modifier}}} {{componentName}} {{#Label}}: {{Label}}{{/Label}} +{{{typeName}}} *-- {{{modifier}}} {{{componentName}}} {{#Label}}: {{Label}}{{/Label}} diff --git a/grammar2uml/src/main/resources/Inheritance.mustache b/grammar2uml/src/main/resources/Inheritance.mustache deleted file mode 100644 index a8f10c0..0000000 --- a/grammar2uml/src/main/resources/Inheritance.mustache +++ /dev/null @@ -1 +0,0 @@ -{{superClassName}} <|-- {{subClassName}} diff --git a/grammar2uml/src/main/resources/Relation.mustache b/grammar2uml/src/main/resources/Relation.mustache index 7f23515..6e9de53 100644 --- a/grammar2uml/src/main/resources/Relation.mustache +++ b/grammar2uml/src/main/resources/Relation.mustache @@ -1 +1 @@ -{{leftName}} {{{leftModifier}}} {{#isBidirectional}}<{{/isBidirectional}}--> {{{rightModifier}}} {{rightName}} {{#Label}}: {{Label}}{{/Label}} +{{{leftName}}} {{{leftModifier}}} {{#isBidirectional}}<{{/isBidirectional}}-[norank]-> {{{rightModifier}}} {{{rightName}}} {{#Label}}: {{Label}}{{/Label}} diff --git a/grammar2uml/src/main/resources/TypeDecl.mustache b/grammar2uml/src/main/resources/TypeDecl.mustache index a72b5dc..4196fea 100644 --- a/grammar2uml/src/main/resources/TypeDecl.mustache +++ b/grammar2uml/src/main/resources/TypeDecl.mustache @@ -1,5 +1,6 @@ -{{=<% %>=}}<%#isAbstract%>abstract <%/isAbstract%>class <%name%><%#InnerTokenComponents%><%#first%> { -<%/first%> - <%name%> -<%#last%> -}<%/last%><%/InnerTokenComponents%><%={{ }}=%> +{{#isAbstract}}abstract {{/isAbstract}}class "{{{Name}}}"{{#backgroundColor}} #{{{backgroundColor}}}{{/backgroundColor}}{{#InnerTokenComponents}}{{#first}} { +{{/first}} + {{{type}}} {{{name}}} +{{#last}} +}{{/last}}{{/InnerTokenComponents}} +{{#hasSuperClass}}{{#SuperClass}}"{{{Name}}}"{{/SuperClass}} <|-- "{{{Name}}}"{{/hasSuperClass}} diff --git a/grammar2uml/src/main/resources/grammar2uml.mustache b/grammar2uml/src/main/resources/grammar2uml.mustache index 6fe27d0..e2b13e3 100644 --- a/grammar2uml/src/main/resources/grammar2uml.mustache +++ b/grammar2uml/src/main/resources/grammar2uml.mustache @@ -6,6 +6,5 @@ hide methods {{#OtherTypes}}{{> TypeDecl}}{{/OtherTypes}} {{#Containments}}{{> Containment}}{{/Containments}} {{#Relations}}{{> Relation}}{{/Relations}} -{{#Inheritances}}{{> Inheritance}}{{/Inheritances}} @enduml ``` -- GitLab