3434import java .util .stream .Collectors ;
3535import java .util .stream .Stream ;
3636
37+ import com .structurizr .export .plantuml .AbstractPlantUMLExporter ;
3738import org .springframework .lang .Nullable ;
3839import org .springframework .modulith .core .ApplicationModule ;
3940import org .springframework .modulith .core .ApplicationModules ;
@@ -553,6 +554,7 @@ private String render(ComponentView view, DiagramOptions options) {
553554 case C4 :
554555
555556 var c4PlantUmlExporter = new C4PlantUMLExporter ();
557+ addSkinParamsFromOptions (c4PlantUmlExporter , options );
556558 var diagram = c4PlantUmlExporter .export (view );
557559
558560 return diagram .getDefinition ();
@@ -561,12 +563,19 @@ private String render(ComponentView view, DiagramOptions options) {
561563 default :
562564
563565 var plantUmlExporter = new CustomizedPlantUmlExporter ();
566+ addSkinParamsFromOptions (plantUmlExporter , options );
564567 plantUmlExporter .addSkinParam ("componentStyle" , "uml1" );
565568
566569 return plantUmlExporter .export (view ).getDefinition ();
567570 }
568571 }
569572
573+ private void addSkinParamsFromOptions (AbstractPlantUMLExporter exporter , DiagramOptions options ) {
574+ for (var skinParamEntry : options .skinParams .entrySet ()) {
575+ exporter .addSkinParam (skinParamEntry .getKey (), skinParamEntry .getValue ());
576+ }
577+ }
578+
570579 private String createPlantUml (DiagramOptions options ) {
571580
572581 ComponentView componentView = createComponentView (options );
@@ -667,6 +676,7 @@ public static class DiagramOptions {
667676 private final Function <ApplicationModule , String > defaultDisplayName ;
668677 private final DiagramStyle style ;
669678 private final ElementsWithoutRelationships elementsWithoutRelationships ;
679+ private final Map <String , String > skinParams ;
670680
671681 /**
672682 * @param dependencyTypes must not be {@literal null}.
@@ -679,13 +689,15 @@ public static class DiagramOptions {
679689 * @param defaultDisplayName must not be {@literal null}.
680690 * @param style must not be {@literal null}.
681691 * @param elementsWithoutRelationships must not be {@literal null}.
692+ * @param skinParams must not be {@literal null}.
682693 */
683694 DiagramOptions (Set <DependencyType > dependencyTypes , DependencyDepth dependencyDepth ,
684695 Predicate <ApplicationModule > exclusions , Predicate <Component > componentFilter ,
685696 Predicate <ApplicationModule > targetOnly , @ Nullable String targetFileName ,
686697 Function <ApplicationModule , Optional <String >> colorSelector ,
687698 Function <ApplicationModule , String > defaultDisplayName , DiagramStyle style ,
688- ElementsWithoutRelationships elementsWithoutRelationships ) {
699+ ElementsWithoutRelationships elementsWithoutRelationships ,
700+ Map <String , String > skinParams ) {
689701
690702 Assert .notNull (dependencyTypes , "Dependency types must not be null!" );
691703 Assert .notNull (dependencyDepth , "Dependency depth must not be null!" );
@@ -696,6 +708,7 @@ public static class DiagramOptions {
696708 Assert .notNull (defaultDisplayName , "Default display name must not be null!" );
697709 Assert .notNull (style , "DiagramStyle must not be null!" );
698710 Assert .notNull (elementsWithoutRelationships , "ElementsWithoutRelationships must not be null!" );
711+ Assert .notNull (skinParams , "SkinParams must not be null!" );
699712
700713 this .dependencyTypes = dependencyTypes ;
701714 this .dependencyDepth = dependencyDepth ;
@@ -707,30 +720,31 @@ public static class DiagramOptions {
707720 this .defaultDisplayName = defaultDisplayName ;
708721 this .style = style ;
709722 this .elementsWithoutRelationships = elementsWithoutRelationships ;
723+ this .skinParams = skinParams ;
710724 }
711725
712726 /**
713727 * The {@link DependencyDepth} to define which other modules to be included in the diagram to be created.
714728 */
715729 public DiagramOptions withDependencyDepth (DependencyDepth dependencyDepth ) {
716730 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
717- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
731+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
718732 }
719733
720734 /**
721735 * A {@link Predicate} to define the which modules to exclude from the diagram to be created.
722736 */
723737 public DiagramOptions withExclusions (Predicate <ApplicationModule > exclusions ) {
724738 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
725- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
739+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
726740 }
727741
728742 /**
729743 * A {@link Predicate} to define which Structurizr {@link Component}s to be included in the diagram to be created.
730744 */
731745 public DiagramOptions withComponentFilter (Predicate <Component > componentFilter ) {
732746 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
733- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
747+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
734748 }
735749
736750 /**
@@ -740,7 +754,7 @@ public DiagramOptions withComponentFilter(Predicate<Component> componentFilter)
740754 */
741755 public DiagramOptions withTargetOnly (Predicate <ApplicationModule > targetOnly ) {
742756 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
743- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
757+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
744758 }
745759
746760 /**
@@ -752,15 +766,15 @@ public DiagramOptions withTargetFileName(String targetFileName) {
752766 Assert .isTrue (targetFileName .contains ("%s" ), () -> INVALID_FILE_NAME_PATTERN .formatted (targetFileName ));
753767
754768 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
755- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
769+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
756770 }
757771
758772 /**
759773 * A callback to return a hex-encoded color per {@link ApplicationModule}.
760774 */
761775 public DiagramOptions withColorSelector (Function <ApplicationModule , Optional <String >> colorSelector ) {
762776 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
763- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
777+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
764778 }
765779
766780 /**
@@ -769,15 +783,15 @@ public DiagramOptions withColorSelector(Function<ApplicationModule, Optional<Str
769783 */
770784 public DiagramOptions withDefaultDisplayName (Function <ApplicationModule , String > defaultDisplayName ) {
771785 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
772- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
786+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
773787 }
774788
775789 /**
776790 * Which style to render the diagram in. Defaults to {@link DiagramStyle#UML}.
777791 */
778792 public DiagramOptions withStyle (DiagramStyle style ) {
779793 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
780- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
794+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
781795 }
782796
783797 /**
@@ -790,7 +804,18 @@ public DiagramOptions withStyle(DiagramStyle style) {
790804 */
791805 public DiagramOptions withElementsWithoutRelationships (ElementsWithoutRelationships elementsWithoutRelationships ) {
792806 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
793- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
807+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
808+ }
809+
810+ /**
811+ * Configuration setting to add arbitrary skin parameters to the created diagrams.
812+ *
813+ * Applies to both the UML and C4 {@link DiagramStyle styles}.
814+ */
815+ public DiagramOptions withSkinParam (String name , String value ) {
816+ skinParams .put (name , value );
817+ return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
818+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
794819 }
795820
796821 /**
@@ -803,7 +828,7 @@ public DiagramOptions withElementsWithoutRelationships(ElementsWithoutRelationsh
803828 public static DiagramOptions defaults () {
804829 return new DiagramOptions (ALL_TYPES , DependencyDepth .IMMEDIATE , it -> false , it -> true , it -> false , null ,
805830 __ -> Optional .empty (), it -> it .getDisplayName (), DiagramStyle .C4 ,
806- ElementsWithoutRelationships .HIDDEN );
831+ ElementsWithoutRelationships .HIDDEN , new LinkedHashMap <>() );
807832 }
808833
809834 /**
@@ -820,7 +845,7 @@ public DiagramOptions withDependencyTypes(DependencyType... types) {
820845
821846 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
822847 targetFileName ,
823- colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
848+ colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
824849 }
825850
826851 private Optional <String > getTargetFileName () {
0 commit comments