Skip to content

Commit 24ca03b

Browse files
committed
Use standard generator architecture for AspectModelDiagramGenerator
1 parent b7cf4f2 commit 24ca03b

File tree

6 files changed

+171
-90
lines changed

6 files changed

+171
-90
lines changed

core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/AspectModelDiagramGenerator.java

Lines changed: 105 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import java.util.stream.Collectors;
3939
import java.util.stream.Stream;
4040

41+
import org.eclipse.esmf.aspectmodel.generator.AspectGenerator;
4142
import org.eclipse.esmf.aspectmodel.generator.DocumentGenerationException;
4243
import org.eclipse.esmf.aspectmodel.generator.LanguageCollector;
4344
import org.eclipse.esmf.metamodel.Aspect;
@@ -59,11 +60,20 @@
5960
/**
6061
* Generate SVG and PNG diagrams from Aspect Models
6162
*/
62-
public class AspectModelDiagramGenerator {
63+
public class AspectModelDiagramGenerator extends AspectGenerator<String, byte[], DiagramGenerationConfig, DiagramArtifact> {
64+
public static final DiagramGenerationConfig DEFAULT_CONFIG = DiagramGenerationConfigBuilder.builder().build();
65+
private static final String FONT_NAME = "Roboto Condensed";
66+
private static final String FONT_FILE = "diagram/RobotoCondensed-Regular.ttf";
67+
68+
/**
69+
* @deprecated Replaced by {@link DiagramGenerationConfig.Format}
70+
*/
71+
@Deprecated( forRemoval = true )
6372
public enum Format {
6473
PNG,
6574
SVG;
6675

76+
@Deprecated( forRemoval = true )
6777
public String getArtifactFilename( final String aspectName, final Locale language ) {
6878
return String.format( "%s_%s.%s", aspectName, language.toLanguageTag(), toString().toLowerCase() );
6979
}
@@ -76,47 +86,33 @@ public String toString() {
7686
};
7787
}
7888

89+
@Deprecated( forRemoval = true )
7990
public static String allValues() {
8091
return String.join( ", ", Stream.of( values() ).map( Format::toString ).toList() );
8192
}
8293
}
8394

84-
private static final String FONT_NAME = "Roboto Condensed";
85-
private static final String FONT_FILE = "diagram/RobotoCondensed-Regular.ttf";
86-
87-
private final Aspect aspect;
88-
8995
public AspectModelDiagramGenerator( final Aspect aspect ) {
90-
this.aspect = aspect;
96+
this( aspect, DEFAULT_CONFIG );
9197
}
9298

93-
InputStream getInputStream( final String resource ) {
94-
return getClass().getClassLoader().getResourceAsStream( resource );
99+
public AspectModelDiagramGenerator( final Aspect aspect, final DiagramGenerationConfig config ) {
100+
super( aspect, config );
95101
}
96102

97-
private void generatePng( final String svgInput, final OutputStream output ) {
98-
// To make the font available during PNG generation, it needs to be registered
99-
// in Java Runtime's graphics environment
100-
try {
101-
final File tmpFontFile = generateTmpFontFile();
102-
final Font f = Font.createFont( Font.TRUETYPE_FONT, tmpFontFile );
103-
final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
104-
ge.registerFont( f );
105-
106-
final String input = svgInput.replaceAll(
107-
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">", "" );
103+
@Override
104+
public Stream<DiagramArtifact> generate() {
105+
final String artifactName = "%s_%s.%s".formatted( aspect().getName(), config.language().toLanguageTag(),
106+
config.format().toString().toLowerCase() );
107+
final String svg = generateSvg();
108+
final byte[] content = config.format() == DiagramGenerationConfig.Format.SVG
109+
? svg.getBytes( StandardCharsets.UTF_8 )
110+
: generatePng( svg );
111+
return Stream.of( new DiagramArtifact( artifactName, content ) );
112+
}
108113

109-
final TranscoderInput inputSvgImage = new TranscoderInput( new StringReader( input ) );
110-
final TranscoderOutput outputPngImage = new TranscoderOutput( output );
111-
final PNGTranscoder pngTranscoder = new PNGTranscoder();
112-
pngTranscoder.transcode( inputSvgImage, outputPngImage );
113-
output.flush();
114-
output.close();
115-
} catch ( final FontFormatException exception ) {
116-
// Will only happen if the loaded .ttf file is invalid
117-
} catch ( final IOException | TranscoderException exception ) {
118-
throw new DocumentGenerationException( exception );
119-
}
114+
private InputStream getInputStream( final String resource ) {
115+
return getClass().getClassLoader().getResourceAsStream( resource );
120116
}
121117

122118
private File generateTmpFontFile() throws IOException {
@@ -141,17 +137,9 @@ private String base64EncodeInputStream( final InputStream in ) throws IOExceptio
141137
}
142138
}
143139

144-
/**
145-
* Generates an SVG diagram for the Aspect in the given target language and write it to the given output stream.
146-
* Note that the document will always be encoded in UTF-8, regardless of the platform's encoding.
147-
*
148-
* @param language the language
149-
* @param out the output stream
150-
* @throws IOException if writing to the output stream fails
151-
*/
152-
public void generateSvg( final Locale language, final OutputStream out ) throws IOException {
153-
final DiagramVisitor diagramVisitor = new DiagramVisitor( language );
154-
final Diagram diagram = aspect.accept( diagramVisitor, Optional.empty() );
140+
private String generateSvg() {
141+
final DiagramVisitor diagramVisitor = new DiagramVisitor( config.language() );
142+
final Diagram diagram = aspect().accept( diagramVisitor, Optional.empty() );
155143
final Graphviz graphviz = render( diagram );
156144

157145
try ( final InputStream fontStream = getInputStream( FONT_FILE ) ) {
@@ -169,14 +157,56 @@ public void generateSvg( final Locale language, final OutputStream out ) throws
169157
+ "\");\n"
170158
+ "}\n"
171159
+ "</style>";
172-
final String result = svgDocument
173-
.replaceFirst( ">", ">" + css );
174-
out.write( result.getBytes( StandardCharsets.UTF_8 ) );
175-
} catch ( final ExecuteException exception ) {
160+
return svgDocument.replaceFirst( ">", ">" + css );
161+
} catch ( final ExecuteException | IOException exception ) {
176162
throw new DocumentGenerationException( exception );
177163
}
178164
}
179165

166+
private byte[] generatePng( final String svg ) {
167+
// To make the font available during PNG generation, it needs to be registered
168+
// in Java Runtime's graphics environment
169+
try {
170+
final File tmpFontFile = generateTmpFontFile();
171+
final Font f = Font.createFont( Font.TRUETYPE_FONT, tmpFontFile );
172+
final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
173+
ge.registerFont( f );
174+
175+
final String input = svg.replaceAll(
176+
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">", "" );
177+
178+
final TranscoderInput inputSvgImage = new TranscoderInput( new StringReader( input ) );
179+
final ByteArrayOutputStream output = new ByteArrayOutputStream();
180+
final TranscoderOutput outputPngImage = new TranscoderOutput( output );
181+
final PNGTranscoder pngTranscoder = new PNGTranscoder();
182+
pngTranscoder.transcode( inputSvgImage, outputPngImage );
183+
output.flush();
184+
output.close();
185+
return output.toByteArray();
186+
} catch ( final FontFormatException exception ) {
187+
// Will only happen if the loaded .ttf file is invalid
188+
throw new DocumentGenerationException( exception );
189+
} catch ( final IOException | TranscoderException exception ) {
190+
throw new DocumentGenerationException( exception );
191+
}
192+
}
193+
194+
/**
195+
* Generates an SVG diagram for the Aspect in the given target language and write it to the given output stream.
196+
* Note that the document will always be encoded in UTF-8, regardless of the platform's encoding.
197+
*
198+
* @param language the language
199+
* @param out the output stream
200+
* @throws IOException if writing to the output stream fails
201+
* @deprecated Use {@link #AspectModelDiagramGenerator(Aspect, DiagramGenerationConfig)} and {@link #generate()} instead
202+
*/
203+
@Deprecated( forRemoval = true )
204+
public void generateSvg( final Locale language, final OutputStream out ) throws IOException {
205+
final DiagramGenerationConfig config = DiagramGenerationConfigBuilder.builder().language( language ).build();
206+
final byte[] content = new AspectModelDiagramGenerator( aspect(), config ).getContent();
207+
IOUtils.copy( new ByteArrayInputStream( content ), out );
208+
}
209+
180210
/**
181211
* Generates a diagram for the Aspect in the given output format and target language.
182212
* Note that SVG documents will always be encoded in UTF-8, regardless of the platform's encoding.
@@ -185,17 +215,17 @@ public void generateSvg( final Locale language, final OutputStream out ) throws
185215
* @param language The language for which the diagram should be generated
186216
* @param out The output stream the diagram is written to
187217
* @throws DocumentGenerationException if diagram generation fails
218+
* @deprecated Use {@link #AspectModelDiagramGenerator(Aspect, DiagramGenerationConfig)} and {@link #generate()} instead
188219
*/
220+
@Deprecated( forRemoval = true )
189221
public void generateDiagram( final Format outputFormat, final Locale language, final OutputStream out ) {
190-
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
222+
final DiagramGenerationConfig config = DiagramGenerationConfigBuilder.builder()
223+
.language( language )
224+
.format( outputFormat == Format.PNG ? DiagramGenerationConfig.Format.PNG : DiagramGenerationConfig.Format.SVG )
225+
.build();
226+
final byte[] content = new AspectModelDiagramGenerator( aspect(), config ).getContent();
191227
try {
192-
generateSvg( language, buffer );
193-
final String svgResult = buffer.toString( StandardCharsets.UTF_8 );
194-
195-
switch ( outputFormat ) {
196-
case PNG -> generatePng( svgResult, out );
197-
default -> IOUtils.copy( new ByteArrayInputStream( svgResult.getBytes( StandardCharsets.UTF_8 ) ), out );
198-
}
228+
IOUtils.copy( new ByteArrayInputStream( content ), out );
199229
} catch ( final IOException exception ) {
200230
throw new DocumentGenerationException( exception );
201231
}
@@ -211,14 +241,18 @@ public void generateDiagram( final Format outputFormat, final Locale language, f
211241
* @param outputFormat One of SVG or PNG
212242
* @param nameMapper The callback function that maps diagram artifact names to OutputStreams
213243
* @throws IOException if a write error occurs
244+
* @deprecated Use {@link #AspectModelDiagramGenerator(Aspect, DiagramGenerationConfig)} and {@link #generate(Function)} instead
214245
*/
246+
@Deprecated( forRemoval = true )
215247
public void generateDiagrams( final Format outputFormat, final Function<String, OutputStream> nameMapper )
216248
throws IOException {
217-
for ( final Locale language : LanguageCollector.collectUsedLanguages( aspect ) ) {
218-
try ( final OutputStream outputStream = nameMapper
219-
.apply( outputFormat.getArtifactFilename( aspect.getName(), language ) ) ) {
220-
generateDiagram( outputFormat, language, outputStream );
221-
}
249+
for ( final Locale language : LanguageCollector.collectUsedLanguages( aspect() ) ) {
250+
final DiagramGenerationConfig config = DiagramGenerationConfigBuilder.builder()
251+
.language( language )
252+
.format( outputFormat == Format.PNG ? DiagramGenerationConfig.Format.PNG : DiagramGenerationConfig.Format.SVG )
253+
.build();
254+
final DiagramArtifact diagramArtifact = new AspectModelDiagramGenerator( aspect(), config ).singleResult();
255+
IOUtils.copy( new ByteArrayInputStream( diagramArtifact.getContent() ), nameMapper.apply( diagramArtifact.getId() ) );
222256
}
223257
}
224258

@@ -233,21 +267,18 @@ public void generateDiagrams( final Format outputFormat, final Function<String,
233267
* @param language The language for which the diagram should be generated
234268
* @param nameMapper The callback function that maps diagram artifact names to OutputStreams
235269
* @throws IOException if a write error occurs
270+
* @deprecated Use {@link #AspectModelDiagramGenerator(Aspect, DiagramGenerationConfig)} and {@link #generate(Function)} instead
236271
*/
272+
@Deprecated( forRemoval = true )
237273
public void generateDiagrams( final Set<Format> targetFormats, final Locale language,
238274
final Function<String, OutputStream> nameMapper ) throws IOException {
239-
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
240-
generateSvg( language, buffer );
241-
final String svgDocument = buffer.toString( StandardCharsets.UTF_8 );
242-
final String aspectName = aspect.getName();
243-
244-
for ( final Format format : targetFormats ) {
245-
try ( final OutputStream outputStream = nameMapper.apply( format.getArtifactFilename( aspectName, language ) ) ) {
246-
switch ( format ) {
247-
case PNG -> generatePng( svgDocument, outputStream );
248-
default -> outputStream.write( svgDocument.getBytes( StandardCharsets.UTF_8 ) );
249-
}
250-
}
275+
for ( final Format outputFormat : targetFormats ) {
276+
final DiagramGenerationConfig config = DiagramGenerationConfigBuilder.builder()
277+
.language( language )
278+
.format( outputFormat == Format.PNG ? DiagramGenerationConfig.Format.PNG : DiagramGenerationConfig.Format.SVG )
279+
.build();
280+
final DiagramArtifact diagramArtifact = new AspectModelDiagramGenerator( aspect(), config ).singleResult();
281+
IOUtils.copy( new ByteArrayInputStream( diagramArtifact.getContent() ), nameMapper.apply( diagramArtifact.getId() ) );
251282
}
252283
}
253284

@@ -261,9 +292,11 @@ public void generateDiagrams( final Set<Format> targetFormats, final Locale lang
261292
* @param targetFormats The set of formats in which diagrams should be generated
262293
* @param nameMapper The callback function that maps diagram artifact names to OutputStreams
263294
* @throws IOException if a write error occurs
295+
* @deprecated Use {@link #AspectModelDiagramGenerator(Aspect, DiagramGenerationConfig)} and {@link #generate(Function)} instead
264296
*/
297+
@Deprecated( forRemoval = true )
265298
public void generateDiagrams( final Set<Format> targetFormats, final Function<String, OutputStream> nameMapper ) throws IOException {
266-
for ( final Locale language : LanguageCollector.collectUsedLanguages( aspect ) ) {
299+
for ( final Locale language : LanguageCollector.collectUsedLanguages( aspect() ) ) {
267300
generateDiagrams( targetFormats, language, nameMapper );
268301
}
269302
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
* Copyright (c) 2024 Bosch Software Innovations GmbH. All rights reserved.
3+
*/
4+
5+
package org.eclipse.esmf.aspectmodel.generator.diagram;
6+
7+
import org.eclipse.esmf.aspectmodel.generator.BinaryArtifact;
8+
9+
public class DiagramArtifact extends BinaryArtifact {
10+
public DiagramArtifact( final String id, final byte[] content ) {
11+
super( id, content );
12+
}
13+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) 2024 Bosch Software Innovations GmbH. All rights reserved.
3+
*/
4+
5+
package org.eclipse.esmf.aspectmodel.generator.diagram;
6+
7+
import java.util.Locale;
8+
import java.util.stream.Stream;
9+
10+
import org.eclipse.esmf.aspectmodel.generator.GenerationConfig;
11+
12+
import io.soabase.recordbuilder.core.RecordBuilder;
13+
14+
@RecordBuilder
15+
public record DiagramGenerationConfig(
16+
Locale language,
17+
Format format
18+
) implements GenerationConfig {
19+
public enum Format {
20+
SVG, PNG;
21+
22+
public static String allValues() {
23+
return String.join( ", ", Stream.of( values() ).map( Format::toString ).toList() );
24+
}
25+
}
26+
27+
public DiagramGenerationConfig {
28+
if ( format == null ) {
29+
format = Format.SVG;
30+
}
31+
if ( language == null ) {
32+
language = Locale.ENGLISH;
33+
}
34+
}
35+
}

core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/FontMeasurement.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ public FlatPoint measure( final String text, final String fontName, final double
4343
final AffineTransform affineTransform = font.getTransform();
4444
final FontRenderContext fontRenderContext = new FontRenderContext( affineTransform, true, true );
4545
final Rectangle2D stringBounds = font.getStringBounds( text, fontRenderContext );
46-
final int width = (int) (stringBounds.getWidth()) + 10;
47-
final int height = (int) (stringBounds.getHeight());
46+
final int width = (int) ( stringBounds.getWidth() ) + 10;
47+
final int height = (int) ( stringBounds.getHeight() );
4848
return new FlatPoint( height, width );
4949
}
5050
}

core/esmf-aspect-model-document-generators/src/test/java/org/eclipse/esmf/aspectmodel/generator/diagram/AspectModelDiagramGeneratorTest.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616
import static org.assertj.core.api.Assertions.assertThat;
1717
import static org.assertj.core.api.Assertions.assertThatCode;
1818

19-
import java.io.ByteArrayOutputStream;
2019
import java.nio.charset.StandardCharsets;
21-
import java.util.Locale;
2220

2321
import org.eclipse.esmf.metamodel.Aspect;
2422
import org.eclipse.esmf.test.TestAspect;
@@ -35,8 +33,7 @@ void testGen( final TestAspect testAspect ) {
3533
final Aspect aspect = TestResources.load( testAspect ).aspect();
3634
final AspectModelDiagramGenerator generator = new AspectModelDiagramGenerator( aspect );
3735
assertThatCode( () -> {
38-
final ByteArrayOutputStream out = new ByteArrayOutputStream();
39-
generator.generateDiagram( AspectModelDiagramGenerator.Format.SVG, Locale.ENGLISH, out );
36+
assertThat( generator.getContent() ).isNotEmpty();
4037
} ).doesNotThrowAnyException();
4138
}
4239

@@ -50,11 +47,10 @@ void generateDiagramsShouldReturnUtf8StringRegardlessOfPlatformEncoding( final S
5047
final AspectModelDiagramGenerator generator = new AspectModelDiagramGenerator( aspect );
5148

5249
assertThatCode( () -> {
53-
final ByteArrayOutputStream out = new ByteArrayOutputStream();
54-
generator.generateDiagram( AspectModelDiagramGenerator.Format.SVG, Locale.ENGLISH, out );
55-
final String svg = out.toString( StandardCharsets.UTF_8 );
50+
final byte[] out = generator.getContent();
51+
final String svg = new String( out, StandardCharsets.UTF_8 );
5652
assertThat( svg ).contains( "«Aspect»" );
57-
assertThat( out.toByteArray() ).containsSubsequence( "«Aspect»".getBytes( StandardCharsets.UTF_8 ) );
53+
assertThat( out ).containsSubsequence( "«Aspect»".getBytes( StandardCharsets.UTF_8 ) );
5854
} ).doesNotThrowAnyException();
5955
} finally {
6056
System.setProperty( "file.encoding", platformEncoding );

0 commit comments

Comments
 (0)