diff --git a/pom.xml b/pom.xml index cd6379e..813466d 100644 --- a/pom.xml +++ b/pom.xml @@ -91,6 +91,12 @@ 5.9.2 test + + org.junit.jupiter + junit-jupiter-params + 5.9.2 + test + org.assertj assertj-core diff --git a/src/main/java/ch/ifocusit/plantuml/PlantUmlBuilder.java b/src/main/java/ch/ifocusit/plantuml/PlantUmlBuilder.java index d7d66ea..bf79436 100644 --- a/src/main/java/ch/ifocusit/plantuml/PlantUmlBuilder.java +++ b/src/main/java/ch/ifocusit/plantuml/PlantUmlBuilder.java @@ -21,6 +21,7 @@ import ch.ifocusit.plantuml.classdiagram.model.Association; import ch.ifocusit.plantuml.classdiagram.model.Association.AssociationType; import ch.ifocusit.plantuml.classdiagram.model.Cardinality; +import ch.ifocusit.plantuml.classdiagram.model.Link; import ch.ifocusit.plantuml.classdiagram.model.Package; import ch.ifocusit.plantuml.classdiagram.model.attribute.Attribute; import ch.ifocusit.plantuml.classdiagram.model.clazz.Clazz; @@ -177,7 +178,7 @@ public PlantUmlBuilder addType(Clazz clazz) { .append(String.join(", ", stereotypes)) .append(STEREOTYPE_CLOSE)); // class link - clazz.getLink().ifPresent(link -> content.append(SPACE).append(link)); + clazz.getLink().ifPresent(link -> content.append(SPACE).append(link.render(Link.LinkContext.CLASS))); // class color clazz.getBackgroundColor().ifPresent(color -> content.append(SPACE).append(color(color))); @@ -192,7 +193,7 @@ public PlantUmlBuilder addType(Clazz clazz) { attribute.getTypeName().ifPresent( type -> content.append(SPACE).append(SEMICOLON).append(SPACE).append(type)); // field link - attribute.getLink().ifPresent(link -> content.append(SPACE).append(link)); + attribute.getLink().ifPresent(link -> content.append(SPACE).append(link.render(Link.LinkContext.FIELD))); content.append(NEWLINE); } // add methods @@ -211,7 +212,7 @@ public PlantUmlBuilder addType(Clazz clazz) { method.getReturnTypeName().ifPresent( type -> content.append(SPACE).append(SEMICOLON).append(SPACE).append(type)); // method link - method.getLink().ifPresent(link -> content.append(SPACE).append(link)); + method.getLink().ifPresent(link -> content.append(SPACE).append(link.render(Link.LinkContext.METHOD))); content.append(NEWLINE); }); if (clazz.hasContent()) { diff --git a/src/main/java/ch/ifocusit/plantuml/classdiagram/model/Link.java b/src/main/java/ch/ifocusit/plantuml/classdiagram/model/Link.java index 300d892..ac1cd63 100644 --- a/src/main/java/ch/ifocusit/plantuml/classdiagram/model/Link.java +++ b/src/main/java/ch/ifocusit/plantuml/classdiagram/model/Link.java @@ -1,30 +1,63 @@ package ch.ifocusit.plantuml.classdiagram.model; +import org.apache.commons.lang3.StringUtils; + @SuppressWarnings("unused") public class Link { private String url; private String label; - - public String getUrl() { - return url; - } + private String tooltip; public void setUrl(String url) { this.url = url; } - public String getLabel() { - return label; - } - public void setLabel(String label) { this.label = label; } - @Override - public String toString() { - return String.format("[[%s{%s}]]", url, label); + public void setTooltip(String tooltip) { + this.tooltip = tooltip; } + public String render(LinkContext context) { + if (LinkContext.CLASS.equals(context)) { + if (StringUtils.isNotBlank(tooltip) && StringUtils.isNotBlank(url)) { + return String.format("[[%s{%s}]]", url, tooltip); + } + if (StringUtils.isNotBlank(tooltip) && StringUtils.isBlank(url)) { + return String.format("[[{%s}]]", tooltip); + } + if (StringUtils.isNotBlank(url)) { + return String.format("[[%s]]", url); + } + return StringUtils.EMPTY; + } + if (StringUtils.isNotBlank(tooltip) && StringUtils.isNotBlank(url) && StringUtils.isNotBlank(label)) { + return String.format("[[[%s{%s} %s]]]", url, tooltip, label); + } + if (StringUtils.isNotBlank(tooltip) && StringUtils.isNotBlank(url) && StringUtils.isBlank(label)) { + return String.format("[[[%s{%s}]]]", url, tooltip); + } + if (StringUtils.isBlank(tooltip) && StringUtils.isNotBlank(url) && StringUtils.isNotBlank(label)) { + return String.format("[[[%s %s]]]", url, label); + } + if (StringUtils.isNotBlank(tooltip) && StringUtils.isBlank(url) && StringUtils.isBlank(label)) { + return String.format("[[[{%s}]]]", tooltip); + } + if (StringUtils.isNotBlank(url) && StringUtils.isBlank(tooltip) && StringUtils.isBlank(label)) { + return String.format("[[[%s]]]", url); + } + if (StringUtils.isNotBlank(url) && StringUtils.isNotBlank(label) && StringUtils.isBlank(tooltip)) { + return String.format("[[[%s %s]]]", url, label); + } + if (StringUtils.isNotBlank(tooltip) && StringUtils.isBlank(url)) { + return String.format("[[[{%s}]]]", tooltip); + } + return StringUtils.EMPTY; + } + public enum LinkContext { + CLASS, FIELD, METHOD + } } diff --git a/src/test/java/ch/ifocusit/plantuml/classdiagram/ClassDiagramBuilderTest.java b/src/test/java/ch/ifocusit/plantuml/classdiagram/ClassDiagramBuilderTest.java index 1c4e703..e260968 100644 --- a/src/test/java/ch/ifocusit/plantuml/classdiagram/ClassDiagramBuilderTest.java +++ b/src/test/java/ch/ifocusit/plantuml/classdiagram/ClassDiagramBuilderTest.java @@ -19,6 +19,7 @@ package ch.ifocusit.plantuml.classdiagram; import ch.ifocusit.plantuml.PlantUmlBuilder; +import ch.ifocusit.plantuml.classdiagram.model.Link; import ch.ifocusit.plantuml.test.helper.domain.Devise; import ch.ifocusit.plantuml.test.helper.domain.Driver; import ch.ifocusit.plantuml.test.helper.domain.Price; @@ -31,9 +32,11 @@ import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; +import java.io.IOException; import java.lang.reflect.Field; import java.nio.charset.Charset; import java.util.Objects; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @@ -47,10 +50,7 @@ class ClassDiagramBuilderTest { @Test void buildShouldGenerateDiagram() throws Exception { - String expected = IOUtils.toString( - Objects.requireNonNull( - this.getClass().getResourceAsStream("/domain-diagram.plantuml")), - Charset.defaultCharset()); + String expected = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/domain-diagram.plantuml")), Charset.defaultCharset()); // tag::createSimple[] String diagram = @@ -137,4 +137,49 @@ public String getFieldName(Field field) { + " attr.brand : String" + CR + " attr.model : String" + CR + " attr.wheels : Collection" + CR + "}" + CR + CR + CR + "@enduml"); } + + @Test + public void testLinkRenderer() throws IOException { + String expected = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/links.puml")), Charset.defaultCharset()); + + String diagram = new ClassDiagramBuilder() + .addClasses(Car.class, Driver.class) + .withLinkMaker(new LinkMaker() { + @Override + public Optional getClassLink(Class aClass) { + String label = aClass.getSimpleName(); + Link link = new Link(); + link.setLabel(label); + link.setUrl("https://link.com/" + label.toLowerCase()); + if (aClass.equals(Driver.class)) { + link.setTooltip("Taxi Driver"); + } + return Optional.of(link); + } + + @Override + public Optional getFieldLink(Field field) { + if (field.getName().equals("wheels") || field.getDeclaringClass().equals(Driver.class)) { + return Optional.empty(); + } + Link link = new Link(); + link.setUrl("https://link.com/car/" + field.getName().toLowerCase()); + if (field.getName().equals("brand") ) { + link.setLabel("lien"); + } + if (field.getName().equals("price") ) { + link.setTooltip("Show details"); + } + if (field.getName().equals("model") ) { + link.setLabel("Car models"); + link.setTooltip("Show all cars' models"); + } + return Optional.of(link); + } + }) + .excludes(".*\\.ignored") + .build(); + + assertThat(diagram).isEqualTo(expected); + } } diff --git a/src/test/java/ch/ifocusit/plantuml/classdiagram/model/LinkTest.java b/src/test/java/ch/ifocusit/plantuml/classdiagram/model/LinkTest.java new file mode 100644 index 0000000..280259b --- /dev/null +++ b/src/test/java/ch/ifocusit/plantuml/classdiagram/model/LinkTest.java @@ -0,0 +1,57 @@ +package ch.ifocusit.plantuml.classdiagram.model; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class LinkTest { + + public static final String URL = "https://link.com"; + public static final String LABEL = "label"; + public static final String TOOLTIP = "tooltip"; + + @ParameterizedTest + @CsvSource({ + ",,,,", + URL + ",,,[[" + URL + "]]", + URL + "," + LABEL + ",,[[" + URL + "]]", + URL + "," + LABEL + "," + TOOLTIP + ",[[" + URL + "{" + TOOLTIP + "}]]", + ",," + TOOLTIP + ",[[{" + TOOLTIP + "}]]", + }) + void render_class(String url, String label, String tooltip, String expected) { + // given + Link link = new Link(); + link.setUrl(url); + link.setLabel(label); + link.setTooltip(tooltip); + // when + String actual = link.render(Link.LinkContext.CLASS); + // then + assertThat(actual).isEqualTo(expected == null ? StringUtils.EMPTY : expected); + } + + @ParameterizedTest + @CsvSource({ + ",,,,", + URL + ",,,[[[" + URL + "]]]", + "," + LABEL + ",,", + ",," + TOOLTIP + ",[[[{" + TOOLTIP + "}]]]", + URL + "," + LABEL + ",,[[[" + URL + " " + LABEL + "]]]", + URL + ",," + TOOLTIP + ",[[[" + URL + "{" + TOOLTIP + "}]]]", + URL + "," + LABEL + "," + TOOLTIP + ",[[[" + URL + "{" + TOOLTIP + "} " + LABEL + "]]]", + "," + LABEL + "," + TOOLTIP + ",[[[{" + TOOLTIP + "}]]]", + }) + void render_field(String url, String label, String tooltip, String expected) { + // given + Link link = new Link(); + link.setUrl(url); + link.setLabel(label); + link.setTooltip(tooltip); + // when + String actual = link.render(Link.LinkContext.FIELD); + // then + assertThat(actual).isEqualTo(expected == null ? StringUtils.EMPTY : expected); + } +} \ No newline at end of file diff --git a/src/test/resources/links.puml b/src/test/resources/links.puml new file mode 100644 index 0000000..a902455 --- /dev/null +++ b/src/test/resources/links.puml @@ -0,0 +1,23 @@ +@startuml + +class "Car" [[https://link.com/car]] { + brand : String [[[https://link.com/car/brand lien]]] + model : String [[[https://link.com/car/model{Show all cars' models} Car models]]] + drivers : Set [[[https://link.com/car/drivers]]] + price : Price [[[https://link.com/car/price{Show details}]]] + wheels : Collection + addDriver(Driver) : Car + addWheel(Wheel) + buyBy(Driver, BigDecimal, Devise) : Driver +} + +class "Driver" [[https://link.com/driver{Taxi Driver}]] { + name : String + cars : List + addCar(Car) + buy(Car) +} + +"Car" "*" <-> "*" "Driver" : drivers/cars + +@enduml \ No newline at end of file