From 13b8f5bf90d0af886056fa03979754207c9e07a6 Mon Sep 17 00:00:00 2001 From: "Textor Andreas (BCI/ESW17)" Date: Thu, 18 Jan 2024 16:23:56 +0100 Subject: [PATCH] Generate UTF-8 encoded diagrams regardless of platform encoding --- .../diagram/AspectModelDiagramGenerator.java | 25 +- .../aspectmodel/generator/diagram/Box.java | 48 --- .../generator/diagram/Context.java | 6 +- .../{AbstractDiagram.java => Diagram.java} | 60 +++- .../generator/diagram/DiagramVisitor.java | 280 +++++++++--------- .../aspectmodel/generator/diagram/Edge.java | 26 -- .../AspectModelDiagramGeneratorTest.java | 25 ++ pom.xml | 2 +- 8 files changed, 240 insertions(+), 232 deletions(-) delete mode 100644 core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Box.java rename core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/{AbstractDiagram.java => Diagram.java} (54%) delete mode 100644 core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Edge.java diff --git a/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/AspectModelDiagramGenerator.java b/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/AspectModelDiagramGenerator.java index 20042a41e..e26c42f0a 100644 --- a/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/AspectModelDiagramGenerator.java +++ b/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/AspectModelDiagramGenerator.java @@ -142,15 +142,20 @@ private String base64EncodeInputStream( final InputStream in ) throws IOExceptio } } - public void generateSvg( final Locale language, final OutputStream out ) - throws IOException { + /** + * Generates an SVG diagram for the Aspect in the given target language and write it to the given output stream. + * Note that the document will always be encoded in UTF-8, regardless of the platform's encoding. + * @param language the language + * @param out the output stream + * @throws IOException if writing to the output stream fails + */ + public void generateSvg( final Locale language, final OutputStream out ) throws IOException { final DiagramVisitor diagramVisitor = new DiagramVisitor( language ); - final AbstractDiagram diagram = aspectContext.aspect().accept( diagramVisitor, Optional.empty() ); + final Diagram diagram = aspectContext.aspect().accept( diagramVisitor, Optional.empty() ); final Graphviz graphviz = render( diagram ); try ( final InputStream fontStream = getInputStream( FONT_FILE ) ) { - final String svgDocument = new String( graphviz.toSvgStr().getBytes( System.getProperty( "file.encoding" ) ), - StandardCharsets.UTF_8 ) + final String svgDocument = graphviz.toSvgStr() .replace( "", "" ) .replace( "", "" ); @@ -174,6 +179,7 @@ public void generateSvg( final Locale language, final OutputStream out ) /** * Generates a diagram for the Aspect in the given output format and target language. + * Note that SVG documents will always be encoded in UTF-8, regardless of the platform's encoding. * * @param outputFormat One of SVG or PNG * @param language The language for which the diagram should be generated @@ -200,6 +206,7 @@ public void generateDiagram( final Format outputFormat, final Locale language, f * The callback function will be called with the name of the diagram, which follows the format * ASPECTNAME_XX.EXT where ASPECTNAME is the samm:name of the Aspect, XX is the language tag * and EXT is the file extension for the respective output format. + * Note that SVG documents will always be encoded in UTF-8, regardless of the platform's encoding. * * @param outputFormat One of SVG or PNG * @param nameMapper The callback function that maps diagram artifact names to OutputStreams @@ -220,6 +227,7 @@ public void generateDiagrams( final Format outputFormat, final Function targetFormats, final Locale lang * The callback function will be called with the name of the diagram, which follows the format * ASPECTNAME_XX.EXT where ASPECTNAME is the samm:name of the Aspect, XX is the language tag * and EXT is the file extension for the respective output format. + * Note that SVG documents will always be encoded in UTF-8, regardless of the platform's encoding. * * @param targetFormats The set of formats in which diagrams should be generated * @param nameMapper The callback function that maps diagram artifact names to OutputStreams @@ -259,16 +268,16 @@ public void generateDiagrams( final Set targetFormats, final Function nodes = new HashMap<>(); + final Map nodes = new HashMap<>(); final Graphviz.GraphvizBuilder graphvizBuilder = Graphviz.digraph() .fontSize( 12f ) .tempNode( Node.builder().shape( NodeShapeEnum.PLAIN ).build() ); - final Map boxMap = diagram.getBoxes() + final Map boxMap = diagram.getBoxes() .stream() .map( box -> { final Html.Table table = table() diff --git a/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Box.java b/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Box.java deleted file mode 100644 index eb8ce5f07..000000000 --- a/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Box.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2023 Robert Bosch Manufacturing Solutions GmbH - * - * See the AUTHORS file(s) distributed with this work for additional - * information regarding authorship. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package org.eclipse.esmf.aspectmodel.generator.diagram; - -import java.util.ArrayList; -import java.util.List; - -public class Box { - private String prototype; - private final String title; - private final List entries = new ArrayList<>(); - - public Box( final String prototype, final String title ) { - this.prototype = prototype; - this.title = title; - } - - public void addEntry( final List entry ) { - entries.addAll( entry ); - } - - public void setPrototype( final String prototype ) { - this.prototype = prototype; - } - - public String getPrototype() { - return prototype; - } - - public String getTitle() { - return title; - } - - public List getEntries() { - return entries; - } -} diff --git a/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Context.java b/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Context.java index 53493f1bb..e40d2e4de 100644 --- a/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Context.java +++ b/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Context.java @@ -14,15 +14,15 @@ package org.eclipse.esmf.aspectmodel.generator.diagram; record Context( - Box parent, + Diagram.Box parent, String prototype, String edgeLabel ) { - Context( final Box parent ) { + Context( final Diagram.Box parent ) { this( parent, "", "" ); } - Context( final Box parent, final String prototype ) { + Context( final Diagram.Box parent, final String prototype ) { this( parent, prototype, "" ); } } diff --git a/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/AbstractDiagram.java b/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Diagram.java similarity index 54% rename from core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/AbstractDiagram.java rename to core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Diagram.java index f0df21d26..50770e840 100644 --- a/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/AbstractDiagram.java +++ b/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Diagram.java @@ -13,11 +13,16 @@ package org.eclipse.esmf.aspectmodel.generator.diagram; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; -class AbstractDiagram { +/** + * An abstract representation of a diagram consisting of {@link Box}es and {@link Edge}s between them + */ +public class Diagram { private final Box focusBox; private final Set boxes; private final Set edges; @@ -25,14 +30,14 @@ class AbstractDiagram { // Used for the special case where a value element is rendered as a string (as part of a parent's attribute) private String scalarValue = null; - public AbstractDiagram( final Box focusBox ) { + public Diagram( final Box focusBox ) { this.focusBox = focusBox; boxes = new HashSet<>(); addBox( focusBox ); edges = new HashSet<>(); } - static final AbstractDiagram EMPTY = new AbstractDiagram( null ); + static final Diagram EMPTY = new Diagram( null ); public void addBox( final Box box ) { if ( box != null ) { @@ -54,9 +59,9 @@ public void addEdges( final Collection edges ) { this.edges.addAll( edges ); } - public void add( final AbstractDiagram abstractDiagram ) { - addBoxes( abstractDiagram.getBoxes() ); - addEdges( abstractDiagram.getEdges() ); + public void add( final Diagram diagram ) { + addBoxes( diagram.getBoxes() ); + addEdges( diagram.getEdges() ); } public void setScalarValue( final String value ) { @@ -78,4 +83,47 @@ public Box getFocusBox() { public String getScalarValue() { return scalarValue; } + + /** + * A box in the diagram with a prototype, title and a list of entries + */ + public static class Box { + private String prototype; + private final String title; + private final List entries = new ArrayList<>(); + + public Box( final String prototype, final String title ) { + this.prototype = prototype; + this.title = title; + } + + public void addEntry( final List entry ) { + entries.addAll( entry ); + } + + public void setPrototype( final String prototype ) { + this.prototype = prototype; + } + + public String getPrototype() { + return prototype; + } + + public String getTitle() { + return title; + } + + public List getEntries() { + return entries; + } + } + + /** + * An edge between two {@link Box}es + * @param from the source box + * @param to the target box + * @param label the label on the edge + */ + public record Edge( Box from, Box to, String label ) { + } } diff --git a/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/DiagramVisitor.java b/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/DiagramVisitor.java index 0989120b5..24ffb7038 100644 --- a/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/DiagramVisitor.java +++ b/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/DiagramVisitor.java @@ -73,25 +73,25 @@ import org.apache.jena.vocabulary.XSD; /** - * AspectVisitor that creates an {@link AbstractDiagram} representation for the given Aspect Model. + * AspectVisitor that creates an {@link Diagram} representation for the given Aspect Model. */ -public class DiagramVisitor implements AspectVisitor> { +public class DiagramVisitor implements AspectVisitor> { private final Locale locale; - private final Map seenElements = new HashMap<>(); + private final Map seenElements = new HashMap<>(); public DiagramVisitor( final Locale locale ) { this.locale = locale; } @Override - public AbstractDiagram visitBase( final ModelElement modelElement, final Optional context ) { - return AbstractDiagram.EMPTY; + public Diagram visitBase( final ModelElement modelElement, final Optional context ) { + return Diagram.EMPTY; } @Override - public AbstractDiagram visitStructureElement( final StructureElement element, final Optional context ) { - final AbstractDiagram result = defaultBox( element, context.orElseThrow().prototype() ); - final Box box = result.getFocusBox(); + public Diagram visitStructureElement( final StructureElement element, final Optional context ) { + final Diagram result = defaultBox( element, context.orElseThrow().prototype() ); + final Diagram.Box box = result.getFocusBox(); for ( final Property property : element.getProperties() ) { final StringBuilder labelBuilder = new StringBuilder(); labelBuilder.append( element instanceof Event ? "parameter" : "property" ); @@ -109,39 +109,39 @@ public AbstractDiagram visitStructureElement( final StructureElement element, fi } @Override - public AbstractDiagram visitAspect( final Aspect aspect, final Optional context ) { + public Diagram visitAspect( final Aspect aspect, final Optional context ) { if ( seenElements.containsKey( aspect ) ) { - return new AbstractDiagram( seenElements.get( aspect ) ); + return new Diagram( seenElements.get( aspect ) ); } - final AbstractDiagram result = visitStructureElement( aspect, Optional.of( new Context( null, "Aspect" ) ) ); - final Box box = result.getFocusBox(); + final Diagram result = visitStructureElement( aspect, Optional.of( new Context( null, "Aspect" ) ) ); + final Diagram.Box box = result.getFocusBox(); aspect.getEvents().stream().map( event -> childElementDiagram( box, event, "event" ) ).forEach( result::add ); aspect.getOperations().stream().map( operation -> childElementDiagram( box, operation, "operation" ) ).forEach( result::add ); return result; } @Override - public AbstractDiagram visitEntity( final Entity entity, final Optional context ) { + public Diagram visitEntity( final Entity entity, final Optional context ) { if ( seenElements.containsKey( entity ) ) { - return new AbstractDiagram( seenElements.get( entity ) ); + return new Diagram( seenElements.get( entity ) ); } - final AbstractDiagram result = visitStructureElement( entity, + final Diagram result = visitStructureElement( entity, context.map( oldContext -> new Context( oldContext.parent(), "Entity" ) ) ); - final Box box = result.getFocusBox(); + final Diagram.Box box = result.getFocusBox(); entity.getExtends().stream().map( superType -> childElementDiagram( box, superType, "extends" ) ).forEach( result::add ); return result; } @Override - public AbstractDiagram visitProperty( final Property property, final Optional context ) { + public Diagram visitProperty( final Property property, final Optional context ) { if ( seenElements.containsKey( property ) ) { - return new AbstractDiagram( seenElements.get( property ) ); + return new Diagram( seenElements.get( property ) ); } - final AbstractDiagram result = defaultBox( property, (property.isAbstract() ? "Abstract" : "") + "Property" ); - final Box box = result.getFocusBox(); + final Diagram result = defaultBox( property, (property.isAbstract() ? "Abstract" : "") + "Property" ); + final Diagram.Box box = result.getFocusBox(); property.getCharacteristic() .filter( characteristic -> !(characteristic.getAspectModelUrn().isEmpty() && characteristic.getName() .equals( "UnnamedCharacteristic" )) ) @@ -153,13 +153,13 @@ public AbstractDiagram visitProperty( final Property property, final Optional context ) { + public Diagram visitCharacteristic( final Characteristic characteristic, final Optional context ) { if ( seenElements.containsKey( characteristic ) ) { - return new AbstractDiagram( seenElements.get( characteristic ) ); + return new Diagram( seenElements.get( characteristic ) ); } - final AbstractDiagram result = defaultBox( characteristic, "Characteristic" ); - final Box box = result.getFocusBox(); + final Diagram result = defaultBox( characteristic, "Characteristic" ); + final Diagram.Box box = result.getFocusBox(); characteristic.getDataType().ifPresent( type -> { if ( type.isScalar() ) { final Scalar scalar = type.as( Scalar.class ); @@ -174,13 +174,13 @@ public AbstractDiagram visitCharacteristic( final Characteristic characteristic, } @Override - public AbstractDiagram visitOperation( final Operation operation, final Optional context ) { + public Diagram visitOperation( final Operation operation, final Optional context ) { if ( seenElements.containsKey( operation ) ) { - return new AbstractDiagram( seenElements.get( operation ) ); + return new Diagram( seenElements.get( operation ) ); } - final AbstractDiagram result = defaultBox( operation, "Operation" ); - final Box box = result.getFocusBox(); + final Diagram result = defaultBox( operation, "Operation" ); + final Diagram.Box box = result.getFocusBox(); for ( final Property property : operation.getInput() ) { result.add( childElementDiagram( box, property, "input" ) ); @@ -190,34 +190,34 @@ public AbstractDiagram visitOperation( final Operation operation, final Optional } @Override - public AbstractDiagram visitAbstractEntity( final AbstractEntity abstractEntity, final Optional context ) { + public Diagram visitAbstractEntity( final AbstractEntity abstractEntity, final Optional context ) { if ( seenElements.containsKey( abstractEntity ) ) { - return new AbstractDiagram( seenElements.get( abstractEntity ) ); + return new Diagram( seenElements.get( abstractEntity ) ); } - final AbstractDiagram result = + final Diagram result = visitStructureElement( abstractEntity, context.map( oldContext -> new Context( oldContext.parent(), "AbstractEntity" ) ) ); - final Box box = result.getFocusBox(); + final Diagram.Box box = result.getFocusBox(); abstractEntity.getExtends().stream().map( superType -> childElementDiagram( box, superType, "extends" ) ).forEach( result::add ); return result; } @Override - public AbstractDiagram visitEvent( final Event event, final Optional context ) { + public Diagram visitEvent( final Event event, final Optional context ) { if ( seenElements.containsKey( event ) ) { - return new AbstractDiagram( seenElements.get( event ) ); + return new Diagram( seenElements.get( event ) ); } return visitStructureElement( event, context.map( oldContext -> new Context( oldContext.parent(), "Event" ) ) ); } @Override - public AbstractDiagram visitUnit( final Unit unit, final Optional context ) { + public Diagram visitUnit( final Unit unit, final Optional context ) { if ( seenElements.containsKey( unit ) ) { - return new AbstractDiagram( seenElements.get( unit ) ); + return new Diagram( seenElements.get( unit ) ); } - final AbstractDiagram result = defaultBox( unit, "Unit" ); - final Box box = result.getFocusBox(); + final Diagram result = defaultBox( unit, "Unit" ); + final Diagram.Box box = result.getFocusBox(); unit.getSymbol().ifPresent( symbol -> box.addEntry( attribute( "symbol", String.class, () -> symbol ) ) ); unit.getReferenceUnit().ifPresent( referenceUnit -> box.addEntry( attribute( "referenceUnit", String.class, () -> referenceUnit ) ) ); @@ -228,13 +228,13 @@ public AbstractDiagram visitUnit( final Unit unit, final Optional conte } @Override - public AbstractDiagram visitTrait( final Trait trait, final Optional context ) { + public Diagram visitTrait( final Trait trait, final Optional context ) { if ( seenElements.containsKey( trait ) ) { - return new AbstractDiagram( seenElements.get( trait ) ); + return new Diagram( seenElements.get( trait ) ); } - final AbstractDiagram result = defaultBox( trait, "Trait" ); - final Box box = result.getFocusBox(); + final Diagram result = defaultBox( trait, "Trait" ); + final Diagram.Box box = result.getFocusBox(); result.add( childElementDiagram( box, trait.getBaseCharacteristic(), "baseCharacteristic" ) ); for ( final Constraint constraint : trait.getConstraints() ) { result.add( childElementDiagram( box, constraint, "constraint" ) ); @@ -243,26 +243,26 @@ public AbstractDiagram visitTrait( final Trait trait, final Optional co } @Override - public AbstractDiagram visitLengthConstraint( final LengthConstraint lengthConstraint, final Optional context ) { + public Diagram visitLengthConstraint( final LengthConstraint lengthConstraint, final Optional context ) { if ( seenElements.containsKey( lengthConstraint ) ) { - return new AbstractDiagram( seenElements.get( lengthConstraint ) ); + return new Diagram( seenElements.get( lengthConstraint ) ); } - final AbstractDiagram result = defaultBox( lengthConstraint, "LengthConstraint" ); - final Box box = result.getFocusBox(); + final Diagram result = defaultBox( lengthConstraint, "LengthConstraint" ); + final Diagram.Box box = result.getFocusBox(); lengthConstraint.getMinValue().ifPresent( minValue -> box.addEntry( attribute( "minValue", BigInteger.class, () -> minValue ) ) ); lengthConstraint.getMinValue().ifPresent( maxValue -> box.addEntry( attribute( "maxValue", BigInteger.class, () -> maxValue ) ) ); return result; } @Override - public AbstractDiagram visitRangeConstraint( final RangeConstraint rangeConstraint, final Optional context ) { + public Diagram visitRangeConstraint( final RangeConstraint rangeConstraint, final Optional context ) { if ( seenElements.containsKey( rangeConstraint ) ) { - return new AbstractDiagram( seenElements.get( rangeConstraint ) ); + return new Diagram( seenElements.get( rangeConstraint ) ); } - final AbstractDiagram result = defaultBox( rangeConstraint, "RangeConstraint" ); - final Box box = result.getFocusBox(); + final Diagram result = defaultBox( rangeConstraint, "RangeConstraint" ); + final Diagram.Box box = result.getFocusBox(); rangeConstraint.getMinValue().ifPresent( minValue -> box.addEntry( attribute( "minValue", ScalarValue.class, () -> minValue ) ) ); rangeConstraint.getMaxValue().ifPresent( maxValue -> box.addEntry( attribute( "maxValue", ScalarValue.class, () -> maxValue ) ) ); if ( rangeConstraint.getLowerBoundDefinition() == BoundDefinition.AT_LEAST @@ -280,98 +280,98 @@ public AbstractDiagram visitRangeConstraint( final RangeConstraint rangeConstrai } @Override - public AbstractDiagram visitFixedPointConstraint( final FixedPointConstraint fixedPointConstraint, final Optional context ) { + public Diagram visitFixedPointConstraint( final FixedPointConstraint fixedPointConstraint, final Optional context ) { if ( seenElements.containsKey( fixedPointConstraint ) ) { - return new AbstractDiagram( seenElements.get( fixedPointConstraint ) ); + return new Diagram( seenElements.get( fixedPointConstraint ) ); } - final AbstractDiagram result = defaultBox( fixedPointConstraint, "FixedPointConstraint" ); - final Box box = result.getFocusBox(); + final Diagram result = defaultBox( fixedPointConstraint, "FixedPointConstraint" ); + final Diagram.Box box = result.getFocusBox(); box.addEntry( attribute( "integer", Integer.class, fixedPointConstraint::getInteger ) ); box.addEntry( attribute( "scale", Integer.class, fixedPointConstraint::getScale ) ); return result; } @Override - public AbstractDiagram visitEncodingConstraint( final EncodingConstraint encodingConstraint, final Optional context ) { + public Diagram visitEncodingConstraint( final EncodingConstraint encodingConstraint, final Optional context ) { if ( seenElements.containsKey( encodingConstraint ) ) { - return new AbstractDiagram( seenElements.get( encodingConstraint ) ); + return new Diagram( seenElements.get( encodingConstraint ) ); } - final AbstractDiagram result = defaultBox( encodingConstraint, "EncodingConstraint" ); - final Box box = result.getFocusBox(); + final Diagram result = defaultBox( encodingConstraint, "EncodingConstraint" ); + final Diagram.Box box = result.getFocusBox(); box.addEntry( attribute( "charset", Charset.class, encodingConstraint::getValue ) ); return result; } @Override - public AbstractDiagram visitLanguageConstraint( final LanguageConstraint languageConstraint, final Optional context ) { + public Diagram visitLanguageConstraint( final LanguageConstraint languageConstraint, final Optional context ) { if ( seenElements.containsKey( languageConstraint ) ) { - return new AbstractDiagram( seenElements.get( languageConstraint ) ); + return new Diagram( seenElements.get( languageConstraint ) ); } - final AbstractDiagram result = defaultBox( languageConstraint, "LanguageConstraint" ); - final Box box = result.getFocusBox(); + final Diagram result = defaultBox( languageConstraint, "LanguageConstraint" ); + final Diagram.Box box = result.getFocusBox(); box.addEntry( attribute( "charset", String.class, () -> languageConstraint.getLanguageCode().toLanguageTag() ) ); return result; } @Override - public AbstractDiagram visitRegularExpressionConstraint( final RegularExpressionConstraint regularExpressionConstraint, + public Diagram visitRegularExpressionConstraint( final RegularExpressionConstraint regularExpressionConstraint, final Optional context ) { if ( seenElements.containsKey( regularExpressionConstraint ) ) { - return new AbstractDiagram( seenElements.get( regularExpressionConstraint ) ); + return new Diagram( seenElements.get( regularExpressionConstraint ) ); } - final AbstractDiagram result = defaultBox( regularExpressionConstraint, "RegularExpressionConstraint" ); - final Box box = result.getFocusBox(); + final Diagram result = defaultBox( regularExpressionConstraint, "RegularExpressionConstraint" ); + final Diagram.Box box = result.getFocusBox(); box.addEntry( attribute( "value", String.class, regularExpressionConstraint::getValue ) ); return result; } @Override - public AbstractDiagram visitQuantifiable( final Quantifiable quantifiable, final Optional context ) { + public Diagram visitQuantifiable( final Quantifiable quantifiable, final Optional context ) { if ( seenElements.containsKey( quantifiable ) ) { - return new AbstractDiagram( seenElements.get( quantifiable ) ); + return new Diagram( seenElements.get( quantifiable ) ); } - final AbstractDiagram result = visitCharacteristic( quantifiable, context ); - final Box box = result.getFocusBox(); + final Diagram result = visitCharacteristic( quantifiable, context ); + final Diagram.Box box = result.getFocusBox(); box.setPrototype( "Quantifiable" ); quantifiable.getUnit().ifPresent( unit -> result.add( childElementDiagram( box, unit, "unit" ) ) ); return result; } @Override - public AbstractDiagram visitMeasurement( final Measurement measurement, final Optional context ) { + public Diagram visitMeasurement( final Measurement measurement, final Optional context ) { if ( seenElements.containsKey( measurement ) ) { - return new AbstractDiagram( seenElements.get( measurement ) ); + return new Diagram( seenElements.get( measurement ) ); } - final AbstractDiagram result = visitQuantifiable( measurement, context ); + final Diagram result = visitQuantifiable( measurement, context ); result.getFocusBox().setPrototype( "Measurement" ); return result; } @Override - public AbstractDiagram visitCode( final Code code, final Optional context ) { + public Diagram visitCode( final Code code, final Optional context ) { if ( seenElements.containsKey( code ) ) { - return new AbstractDiagram( seenElements.get( code ) ); + return new Diagram( seenElements.get( code ) ); } - final AbstractDiagram result = visitCharacteristic( (Characteristic) code, context ); - final Box box = result.getFocusBox(); + final Diagram result = visitCharacteristic( (Characteristic) code, context ); + final Diagram.Box box = result.getFocusBox(); box.setPrototype( "Code" ); return result; } @Override - public AbstractDiagram visitCollection( final Collection collection, final Optional context ) { + public Diagram visitCollection( final Collection collection, final Optional context ) { if ( seenElements.containsKey( collection ) ) { - return new AbstractDiagram( seenElements.get( collection ) ); + return new Diagram( seenElements.get( collection ) ); } - final AbstractDiagram result; + final Diagram result; if ( collection.getElementCharacteristic().isPresent() ) { // If the collection has an elementCharacteristic, don't use visitCharacteristic to prevent additional dataType edge result = defaultBox( collection, "Collection" ); @@ -380,70 +380,70 @@ public AbstractDiagram visitCollection( final Collection collection, final Optio result.getFocusBox().setPrototype( "Collection" ); } - final Box box = result.getFocusBox(); + final Diagram.Box box = result.getFocusBox(); collection.getElementCharacteristic().ifPresent( elementCharacteristic -> result.add( childElementDiagram( box, elementCharacteristic, "elementCharacteristic" ) ) ); return result; } @Override - public AbstractDiagram visitList( final org.eclipse.esmf.characteristic.List list, final Optional context ) { + public Diagram visitList( final org.eclipse.esmf.characteristic.List list, final Optional context ) { if ( seenElements.containsKey( list ) ) { - return new AbstractDiagram( seenElements.get( list ) ); + return new Diagram( seenElements.get( list ) ); } - final AbstractDiagram result = visitCollection( list, context ); - final Box box = result.getFocusBox(); + final Diagram result = visitCollection( list, context ); + final Diagram.Box box = result.getFocusBox(); box.setPrototype( "List" ); return result; } @Override - public AbstractDiagram visitSet( final Set set, final Optional context ) { + public Diagram visitSet( final Set set, final Optional context ) { if ( seenElements.containsKey( set ) ) { - return new AbstractDiagram( seenElements.get( set ) ); + return new Diagram( seenElements.get( set ) ); } - final AbstractDiagram result = visitCollection( set, context ); - final Box box = result.getFocusBox(); + final Diagram result = visitCollection( set, context ); + final Diagram.Box box = result.getFocusBox(); box.setPrototype( "Set" ); return result; } @Override - public AbstractDiagram visitSortedSet( final SortedSet sortedSet, final Optional context ) { + public Diagram visitSortedSet( final SortedSet sortedSet, final Optional context ) { if ( seenElements.containsKey( sortedSet ) ) { - return new AbstractDiagram( seenElements.get( sortedSet ) ); + return new Diagram( seenElements.get( sortedSet ) ); } - final AbstractDiagram result = visitCollection( sortedSet, context ); - final Box box = result.getFocusBox(); + final Diagram result = visitCollection( sortedSet, context ); + final Diagram.Box box = result.getFocusBox(); box.setPrototype( "SortedSet" ); return result; } @Override - public AbstractDiagram visitDuration( final Duration duration, final Optional context ) { + public Diagram visitDuration( final Duration duration, final Optional context ) { if ( seenElements.containsKey( duration ) ) { - return new AbstractDiagram( seenElements.get( duration ) ); + return new Diagram( seenElements.get( duration ) ); } - final AbstractDiagram result = visitQuantifiable( duration, context ); - final Box box = result.getFocusBox(); + final Diagram result = visitQuantifiable( duration, context ); + final Diagram.Box box = result.getFocusBox(); box.setPrototype( "Duration" ); return result; } @Override - public AbstractDiagram visitEnumeration( final Enumeration enumeration, final Optional context ) { + public Diagram visitEnumeration( final Enumeration enumeration, final Optional context ) { if ( seenElements.containsKey( enumeration ) ) { - return new AbstractDiagram( seenElements.get( enumeration ) ); + return new Diagram( seenElements.get( enumeration ) ); } - final AbstractDiagram result = visitCharacteristic( enumeration, context ); - final Box box = result.getFocusBox(); + final Diagram result = visitCharacteristic( enumeration, context ); + final Diagram.Box box = result.getFocusBox(); box.setPrototype( "Enumeration" ); - final AbstractDiagram collectionDiagram = processValuesCollection( box, enumeration.getValues(), "value" ); + final Diagram collectionDiagram = processValuesCollection( box, enumeration.getValues(), "value" ); if ( collectionDiagram.getScalarValue() == null ) { result.add( collectionDiagram ); } else { @@ -453,46 +453,46 @@ public AbstractDiagram visitEnumeration( final Enumeration enumeration, final Op } @Override - public AbstractDiagram visitEither( final Either either, final Optional context ) { - final AbstractDiagram result = defaultBox( either, "Either" ); - final Box box = result.getFocusBox(); + public Diagram visitEither( final Either either, final Optional context ) { + final Diagram result = defaultBox( either, "Either" ); + final Diagram.Box box = result.getFocusBox(); result.add( childElementDiagram( box, either.getLeft(), "left" ) ); result.add( childElementDiagram( box, either.getRight(), "right" ) ); return result; } @Override - public AbstractDiagram visitSingleEntity( final SingleEntity singleEntity, final Optional context ) { + public Diagram visitSingleEntity( final SingleEntity singleEntity, final Optional context ) { if ( seenElements.containsKey( singleEntity ) ) { - return new AbstractDiagram( seenElements.get( singleEntity ) ); + return new Diagram( seenElements.get( singleEntity ) ); } - final AbstractDiagram result = visitCharacteristic( (Characteristic) singleEntity, context ); - final Box box = result.getFocusBox(); + final Diagram result = visitCharacteristic( (Characteristic) singleEntity, context ); + final Diagram.Box box = result.getFocusBox(); box.setPrototype( "SingleEntity" ); return result; } @Override - public AbstractDiagram visitState( final State state, final Optional context ) { - final AbstractDiagram result = visitEnumeration( state, context ); - final Box box = result.getFocusBox(); + public Diagram visitState( final State state, final Optional context ) { + final Diagram result = visitEnumeration( state, context ); + final Diagram.Box box = result.getFocusBox(); box.setPrototype( "State" ); - final AbstractDiagram valueDiagram = state.getDefaultValue().accept( this, Optional.of( new Context( box ) ) ); + final Diagram valueDiagram = state.getDefaultValue().accept( this, Optional.of( new Context( box ) ) ); result.add( valueDiagram ); if ( valueDiagram.getScalarValue() != null ) { box.addEntry( attribute( "defaultValue", String.class, valueDiagram::getScalarValue ) ); } if ( valueDiagram.getFocusBox() != null ) { - result.addEdge( new Edge( box, valueDiagram.getFocusBox(), "defaultValue" ) ); + result.addEdge( new Diagram.Edge( box, valueDiagram.getFocusBox(), "defaultValue" ) ); } return result; } @Override - public AbstractDiagram visitStructuredValue( final StructuredValue structuredValue, final Optional context ) { - final AbstractDiagram result = visitCharacteristic( (Characteristic) structuredValue, context ); - final Box box = result.getFocusBox(); + public Diagram visitStructuredValue( final StructuredValue structuredValue, final Optional context ) { + final Diagram result = visitCharacteristic( (Characteristic) structuredValue, context ); + final Diagram.Box box = result.getFocusBox(); box.setPrototype( "StructuredValue" ); int index = 1; for ( final Object element : structuredValue.getElements() ) { @@ -507,16 +507,16 @@ public AbstractDiagram visitStructuredValue( final StructuredValue structuredVal } @Override - public AbstractDiagram visitTimeSeries( final TimeSeries timeSeries, final Optional context ) { - final AbstractDiagram result = visitSortedSet( timeSeries, context ); - final Box box = result.getFocusBox(); + public Diagram visitTimeSeries( final TimeSeries timeSeries, final Optional context ) { + final Diagram result = visitSortedSet( timeSeries, context ); + final Diagram.Box box = result.getFocusBox(); box.setPrototype( "TimeSeries" ); return result; } @Override - public AbstractDiagram visitScalarValue( final ScalarValue value, final Optional context ) { - final AbstractDiagram result = new AbstractDiagram( null ); + public Diagram visitScalarValue( final ScalarValue value, final Optional context ) { + final Diagram result = new Diagram( null ); if ( value.getValue() instanceof final LangString langString ) { result.setScalarValue( "\"%s\"@%s".formatted( langString.getValue(), langString.getLanguageTag() ) ); } else { @@ -526,31 +526,31 @@ public AbstractDiagram visitScalarValue( final ScalarValue value, final Optional } @Override - public AbstractDiagram visitCollectionValue( final CollectionValue collection, final Optional context ) { + public Diagram visitCollectionValue( final CollectionValue collection, final Optional context ) { return context.map( parentContext -> processValuesCollection( parentContext.parent(), collection.getValues(), - Optional.ofNullable( parentContext.edgeLabel() ).orElse( "value" ) ) ).orElse( AbstractDiagram.EMPTY ); + Optional.ofNullable( parentContext.edgeLabel() ).orElse( "value" ) ) ).orElse( Diagram.EMPTY ); } @Override - public AbstractDiagram visitEntityInstance( final EntityInstance instance, final Optional context ) { + public Diagram visitEntityInstance( final EntityInstance instance, final Optional context ) { if ( seenElements.containsKey( instance ) ) { - return new AbstractDiagram( seenElements.get( instance ) ); + return new Diagram( seenElements.get( instance ) ); } - final AbstractDiagram result = defaultBox( instance, instance.getEntityType().getName() ); - final Box box = result.getFocusBox(); + final Diagram result = defaultBox( instance, instance.getEntityType().getName() ); + final Diagram.Box box = result.getFocusBox(); result.add( childElementDiagram( box, instance.getEntityType(), "type" ) ); for ( final Map.Entry assertion : instance.getAssertions().entrySet() ) { final Property property = assertion.getKey(); final String propertyName = property.getName() + (property.getPayloadName().equals( property.getName() ) ? "" : " (%s)".formatted( property.getPayloadName() )); - final AbstractDiagram valueDiagram = assertion.getValue().accept( this, Optional.of( new Context( box, "", propertyName ) ) ); + final Diagram valueDiagram = assertion.getValue().accept( this, Optional.of( new Context( box, "", propertyName ) ) ); if ( valueDiagram.getScalarValue() == null ) { // If the value's diagram representation's scalar value is not set, the value is itself a box result.add( valueDiagram ); // If the value is a collection, it doesn't have a focus box if ( valueDiagram.getFocusBox() != null ) { - result.addEdge( new Edge( box, valueDiagram.getFocusBox(), propertyName ) ); + result.addEdge( new Diagram.Edge( box, valueDiagram.getFocusBox(), propertyName ) ); } } else { // If the value's diagram representation's scalar value is set, use it for an attribute entry in the entity instance's box @@ -561,16 +561,16 @@ public AbstractDiagram visitEntityInstance( final EntityInstance instance, final return result; } - private > AbstractDiagram processValuesCollection( final Box parentBox, final T collection, + private > Diagram processValuesCollection( final Diagram.Box parentBox, final T collection, final String edgeLabel ) { - final AbstractDiagram result = new AbstractDiagram( null ); + final Diagram result = new Diagram( null ); boolean hasScalarValues = false; final List scalarValues = new ArrayList<>(); for ( final Value value : collection ) { - final AbstractDiagram valueDiagram = value.accept( this, Optional.of( new Context( parentBox ) ) ); + final Diagram valueDiagram = value.accept( this, Optional.of( new Context( parentBox ) ) ); if ( valueDiagram.getScalarValue() == null ) { result.add( valueDiagram ); - result.addEdge( new Edge( parentBox, valueDiagram.getFocusBox(), edgeLabel ) ); + result.addEdge( new Diagram.Edge( parentBox, valueDiagram.getFocusBox(), edgeLabel ) ); } else { hasScalarValues = true; scalarValues.add( valueDiagram.getScalarValue() ); @@ -582,15 +582,15 @@ private > AbstractDiagram processValuesCol return result; } - private AbstractDiagram childElementDiagram( final Box parent, final ModelElement child, final String edgeLabel ) { + private Diagram childElementDiagram( final Diagram.Box parent, final ModelElement child, final String edgeLabel ) { final Optional childElementContext = Optional.of( new Context( parent ) ); - final AbstractDiagram result = child.accept( this, childElementContext ); - result.addEdge( new Edge( parent, result.getFocusBox(), edgeLabel ) ); + final Diagram result = child.accept( this, childElementContext ); + result.addEdge( new Diagram.Edge( parent, result.getFocusBox(), edgeLabel ) ); return result; } - private AbstractDiagram defaultBox( final NamedElement element, final String prototype ) { - final Box box = new Box( prototype, element.getAspectModelUrn().isPresent() ? element.getName() : "" ); + private Diagram defaultBox( final NamedElement element, final String prototype ) { + final Diagram.Box box = new Diagram.Box( prototype, element.getAspectModelUrn().isPresent() ? element.getName() : "" ); final ImmutableList.Builder standardAttributes = ImmutableList.builder(); element.getPreferredNames().stream() .filter( preferredName -> preferredName.getLanguageTag().equals( locale ) ) @@ -609,7 +609,7 @@ private AbstractDiagram defaultBox( final NamedElement element, final String pro } box.addEntry( standardAttributes.build() ); seenElements.put( element, box ); - return new AbstractDiagram( box ); + return new Diagram( box ); } private List attribute( final String attributeName, final Class type, final Supplier attribute ) { diff --git a/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Edge.java b/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Edge.java deleted file mode 100644 index efd7bf05f..000000000 --- a/core/esmf-aspect-model-document-generators/src/main/java/org/eclipse/esmf/aspectmodel/generator/diagram/Edge.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2023 Robert Bosch Manufacturing Solutions GmbH - * - * See the AUTHORS file(s) distributed with this work for additional - * information regarding authorship. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package org.eclipse.esmf.aspectmodel.generator.diagram; - -record Edge( - Box from, - Box to, - String label -) { - Edge( final Box from, final Box to, final String label ) { - this.from = from; - this.to = to; - this.label = label; - } -} diff --git a/core/esmf-aspect-model-document-generators/src/test/java/org/eclipse/esmf/aspectmodel/generator/diagram/AspectModelDiagramGeneratorTest.java b/core/esmf-aspect-model-document-generators/src/test/java/org/eclipse/esmf/aspectmodel/generator/diagram/AspectModelDiagramGeneratorTest.java index 3e780e531..61b9520c5 100644 --- a/core/esmf-aspect-model-document-generators/src/test/java/org/eclipse/esmf/aspectmodel/generator/diagram/AspectModelDiagramGeneratorTest.java +++ b/core/esmf-aspect-model-document-generators/src/test/java/org/eclipse/esmf/aspectmodel/generator/diagram/AspectModelDiagramGeneratorTest.java @@ -13,9 +13,11 @@ package org.eclipse.esmf.aspectmodel.generator.diagram; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; import java.util.Locale; import org.eclipse.esmf.aspectmodel.resolver.services.VersionedModel; @@ -29,6 +31,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; public class AspectModelDiagramGeneratorTest extends MetaModelVersions { @ParameterizedTest @@ -43,4 +46,26 @@ void testGen( final TestAspect testAspect ) { generator.generateDiagram( AspectModelDiagramGenerator.Format.SVG, Locale.ENGLISH, out ); } ).doesNotThrowAnyException(); } + + @ParameterizedTest + @ValueSource( strings = { "UTF-8", "US-ASCII" } ) + void generateDiagramsShouldReturnUTF8StringRegardlessOfPlatformEncoding( final String encoding ) throws Exception { + final String platformEncoding = System.getProperty( "file.encoding" ); + try { + System.setProperty( "file.encoding", encoding ); + final VersionedModel versionedModel = TestResources.getModel( TestAspect.ASPECT, KnownVersion.getLatest() ).get(); + final Aspect aspect = AspectModelLoader.getSingleAspect( versionedModel ).getOrElseThrow( () -> new RuntimeException() ); + final AspectContext context = new AspectContext( versionedModel, aspect ); + final AspectModelDiagramGenerator generator = new AspectModelDiagramGenerator( context ); + + assertThatCode( () -> { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + generator.generateDiagram( AspectModelDiagramGenerator.Format.SVG, Locale.ENGLISH, out ); + final String svg = out.toString( StandardCharsets.UTF_8 ); + assertThat( svg ).contains( "«Aspect»" ); + } ).doesNotThrowAnyException(); + } finally { + System.setProperty( "file.encoding", platformEncoding ); + } + } } diff --git a/pom.xml b/pom.xml index fb51923dc..3333614a1 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ org.eclipse.esmf esmf-parent - 10 + 11 esmf-sdk-parent