This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/master by this push:
     new 75155849cb CAUSEWAY-3404: quality of life improvements for ObjectGraph
75155849cb is described below

commit 75155849cb216c3c87f271962aa71f1ad32d6e83
Author: Andi Huber <[email protected]>
AuthorDate: Fri Jan 19 07:53:22 2024 +0100

    CAUSEWAY-3404: quality of life improvements for ObjectGraph
    
    - adds object and field descriptions
    - export utilities
    - also adds some string utilities
---
 .../services/metamodel/objgraph/ObjectGraph.java   | 56 +++++++++++++++++-----
 .../objgraph/_ObjectGraphRelationMerger.java       | 35 ++++++++++----
 .../causeway/commons/internal/base/_Strings.java   | 36 ++++++++++++--
 .../org/apache/causeway/commons/io/DataSource.java |  9 ++++
 .../services/metamodel/_ObjectGraphFactory.java    | 14 ++++--
 .../objgraph/d3js/ObjectGraphRendererD3js.java     |  2 +-
 .../plantuml/ObjectGraphRendererPlantuml.java      |  5 +-
 7 files changed, 126 insertions(+), 31 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/objgraph/ObjectGraph.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/objgraph/ObjectGraph.java
index ed18837836..be43ee484e 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/objgraph/ObjectGraph.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/objgraph/ObjectGraph.java
@@ -18,6 +18,8 @@
  */
 package org.apache.causeway.applib.services.metamodel.objgraph;
 
+import java.io.File;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -26,7 +28,11 @@ import java.util.Optional;
 
 import org.springframework.lang.Nullable;
 
+import org.apache.causeway.commons.internal.base._Strings;
+import org.apache.causeway.commons.internal.base._Strings.StringOperator;
 import org.apache.causeway.commons.internal.collections._Multimaps;
+import org.apache.causeway.commons.io.DataSink;
+import org.apache.causeway.commons.io.DataSource;
 
 import lombok.Getter;
 import lombok.NonNull;
@@ -49,6 +55,7 @@ public class ObjectGraph {
         private final @With @NonNull String packageName;
         private final @With @NonNull String name;
         private final @With @NonNull Optional<String> stereotype;
+        private final @With @NonNull Optional<String> description;
         private final @With List<ObjectGraph.Field> fields;
     }
 
@@ -57,6 +64,7 @@ public class ObjectGraph {
         private final @With @NonNull String name;
         private final @With @NonNull String elementTypeShortName;
         private final @With boolean isPlural;
+        private final @With @NonNull Optional<String> description;
     }
 
     @lombok.Value @Accessors(fluent=true)
@@ -64,27 +72,34 @@ public class ObjectGraph {
         private final @With @NonNull RelationType relationType;
         private final @With @NonNull ObjectGraph.Object from;
         private final @With @NonNull ObjectGraph.Object to;
-        private final @With @NonNull String label;
-        private final @With @NonNull String label2;
+        private final @With @NonNull String description; // usually the middle 
label
+        private final @With @NonNull String nearLabel;
+        private final @With @NonNull String farLabel;
         public String fromId() { return from.id(); }
         public String toId() { return to.id(); }
-        public boolean isAssociation() { return 
relationType!=RelationType.INHERITANCE; }
-        /**
-         * If this is ONE_TO_MANY, decorate the label with enclosing square 
brackets.
-         */
-        public String labelFormatted() {
-            return relationType==RelationType.ONE_TO_MANY
-                    ? String.format("[%s]", label)
-                    : label;
+        public boolean isAssociation() { return 
relationType.isAssociationAny(); }
+        public StringOperator multiplicityNotation() {
+            return relationType.isOneToMany()
+                    ? _Strings.asSquareBracketed
+                    : StringOperator.identity();
+        }
+        public String descriptionFormatted() {
+            return multiplicityNotation().apply(description);
         }
     }
 
-    public static enum RelationType {
+    public enum RelationType {
         ONE_TO_ONE,
         ONE_TO_MANY,
         MERGED_ASSOCIATIONS,
         BIDIR_ASSOCIATION,
         INHERITANCE;
+        public boolean isOneToOne() { return this == ONE_TO_ONE; }
+        public boolean isOneToMany() { return this == ONE_TO_MANY; }
+        public boolean isMerged() { return this == MERGED_ASSOCIATIONS; }
+        public boolean isBidir() { return this == BIDIR_ASSOCIATION; }
+        public boolean isInheritance() { return this == INHERITANCE; }
+        public boolean isAssociationAny() { return this != INHERITANCE; }
     }
 
     public static interface Factory {
@@ -127,6 +142,25 @@ public class ObjectGraph {
         renderer.render(sb, this);
         return sb.toString();
     }
+    public DataSource asDiagramDslSource(final @Nullable ObjectGraph.Renderer 
renderer) {
+        var dsl = render(renderer);
+        return dsl==null
+                ? DataSource.empty()
+                : DataSource.ofStringUtf8(dsl);
+    }
+    public void writeDiagramDsl(final @Nullable ObjectGraph.Renderer renderer, 
final DataSink sink) {
+        var dsl = render(renderer);
+        if(dsl==null) return;
+        sink.writeAll(os->
+            os.write(dsl.getBytes(StandardCharsets.UTF_8)));
+    }
+    public void writeDiagramDsl(final @Nullable ObjectGraph.Renderer renderer, 
final File destinationDslFile) {
+        var dsl = render(renderer);
+        if(dsl==null) return;
+        DataSink.ofFile(destinationDslFile)
+            .writeAll(os->
+                os.write(dsl.getBytes(StandardCharsets.UTF_8)));
+    }
 
     /**
      * Returns objects grouped by package (as list-multimap).
diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/objgraph/_ObjectGraphRelationMerger.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/objgraph/_ObjectGraphRelationMerger.java
index 04bdeb59ab..a947cd54f6 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/objgraph/_ObjectGraphRelationMerger.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/objgraph/_ObjectGraphRelationMerger.java
@@ -21,6 +21,7 @@ package 
org.apache.causeway.applib.services.metamodel.objgraph;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import org.apache.causeway.commons.internal.collections._Multimaps;
 import 
org.apache.causeway.commons.internal.collections._Multimaps.ListMultimap;
@@ -61,11 +62,20 @@ class _ObjectGraphRelationMerger implements 
ObjectGraph.Transformer {
         relations.removeIf(ObjectGraph.Relation::isAssociation);
 
         shared.forEach((key, list) -> {
-            val merged = list.stream().reduce((a, b)->new ObjectGraph.Relation(
+            if(list.size()<2) return;
+
+            var mergedDescriptions = list.stream()
+                    .map(rel->rel.descriptionFormatted())
+                    .collect(Collectors.joining(","));
+
+            var a = list.get(0);
+            var merged = new ObjectGraph.Relation(
                     ObjectGraph.RelationType.MERGED_ASSOCIATIONS,
-                    objectById.get(a.fromId()), objectById.get(a.toId()),
-                    a.labelFormatted() + "," + b.labelFormatted(), ""));
-            merged.ifPresent(relations::add);
+                    objectById.get(a.fromId()),
+                    objectById.get(a.toId()),
+                    mergedDescriptions, // already formatted honoring 
multiplicity notation
+                    "", "");
+            relations.add(merged);
         });
 
     }
@@ -92,12 +102,21 @@ class _ObjectGraphRelationMerger implements 
ObjectGraph.Transformer {
         shared.forEach((key, list) -> {
             if(list.size()==2) {
                 relations.removeAll(list);
-                val a = list.get(0);
-                val b = list.get(1);
+                var a = list.get(0);
+                var b = list.get(1);
+                // near label receives b's description
+                // far label receives a's description
+                // (formatting has no effect on 
RelationType.MERGED_ASSOCIATIONS)
+                var nearLabel = b.descriptionFormatted();
+                var farLabel = a.descriptionFormatted();
+
                 relations.add(new ObjectGraph.Relation(
                         ObjectGraph.RelationType.BIDIR_ASSOCIATION,
-                        objectById.get(a.fromId()), objectById.get(a.toId()),
-                        a.labelFormatted(), b.labelFormatted()));
+                        objectById.get(a.fromId()),
+                        objectById.get(a.toId()),
+                        "", // middle label is cleared
+                        nearLabel,
+                        farLabel));
             }
         });
     }
diff --git 
a/commons/src/main/java/org/apache/causeway/commons/internal/base/_Strings.java 
b/commons/src/main/java/org/apache/causeway/commons/internal/base/_Strings.java
index 071020fdcc..916a9881f5 100644
--- 
a/commons/src/main/java/org/apache/causeway/commons/internal/base/_Strings.java
+++ 
b/commons/src/main/java/org/apache/causeway/commons/internal/base/_Strings.java
@@ -421,6 +421,16 @@ public final class _Strings {
                 : input;
     }
 
+    /**
+     * Returns a {@link StringOperator} that converts given literal into 
{@code prefix + literal + suffix} ,
+     * unless the literal is {@code null}, in which case the operator returns 
{@code null}.
+     */
+    public static final StringOperator bracketed(final @NonNull String prefix, 
final @NonNull String suffix) {
+        return s->s!=null
+                ? prefix + s + suffix
+                : null;
+    }
+
     // -- REDUCTION (BINARY OPERATIOR)
 
     /**
@@ -827,14 +837,30 @@ public final class _Strings {
     // using naming convention asXxx...
 
     public static final StringOperator asLowerCase = _Strings::lower;
+    public static final StringOperator asUpperCase = _Strings::upper;
+    public static final StringOperator asCapitalized = _Strings::capitalize;
+    public static final StringOperator asDecapitalized = 
_Strings::decapitalize;
 
     /**
      * Converts given literal into a double-quoted literal, unless the literal 
is {@code null},
      * in which case {@code null} is returned.
      */
-    public static final StringOperator asDoubleQuoted = s->s!=null
-            ? "\"" + s + "\""
-            : null;
+    public static final StringOperator asDoubleQuoted = bracketed("\"", "\"");
+    /**
+     * Converts given literal into a square-bracketed literal, unless the 
literal is {@code null},
+     * in which case {@code null} is returned.
+     */
+    public static final StringOperator asSquareBracketed = bracketed("[", "]");
+    /**
+     * Converts given literal into a parenthesis-bracketed literal, unless the 
literal is {@code null},
+     * in which case {@code null} is returned.
+     */
+    public static final StringOperator asParenthesisBracketed = bracketed("(", 
")");
+    /**
+     * Converts given literal into a curly-bracketed literal, unless the 
literal is {@code null},
+     * in which case {@code null} is returned.
+     */
+    public static final StringOperator asCurlyBracketed = bracketed("{", "}");
 
     public static final StringOperator asLowerDashed = asLowerCase
             .compose(s->_Strings.condenseWhitespaces(s, "-"));
@@ -859,10 +885,10 @@ public final class _Strings {
             s->_Strings_CamelCase.camelCase(s, firstToken->firstToken);
 
     public static final StringOperator asCamelCaseDecapitalized =
-            s->_Strings_CamelCase.camelCase(s, 
firstToken->_Strings.decapitalize(firstToken));
+            s->_Strings_CamelCase.camelCase(s, asDecapitalized);
 
     public static final StringOperator asCamelCaseCapitalized =
-            s->_Strings_CamelCase.camelCase(s, 
firstToken->_Strings.capitalize(firstToken));
+            s->_Strings_CamelCase.camelCase(s, asCapitalized);
     public static final StringOperator asPascalCase = asCamelCaseCapitalized; 
// synonym
 
     public static final String asFileNameWithExtension(final @NonNull String 
fileName, final @NonNull String fileExtension) {
diff --git 
a/commons/src/main/java/org/apache/causeway/commons/io/DataSource.java 
b/commons/src/main/java/org/apache/causeway/commons/io/DataSource.java
index f11203d917..e3cdb15bb7 100644
--- a/commons/src/main/java/org/apache/causeway/commons/io/DataSource.java
+++ b/commons/src/main/java/org/apache/causeway/commons/io/DataSource.java
@@ -202,6 +202,15 @@ public interface DataSource {
     default void pipe(final @NonNull DataSink dataSink, final int bufferSize) {
         tryReadAndWrite(dataSink, bufferSize).ifFailureFail();
     }
+    /**
+     * Acts as a pipe, reading from this {@link DataSource} and writing to 
given {@link DataSink},
+     * using default bufferSize of 16k for the underlying byte data junks.
+     * <p>
+     * Throws if the write failed.
+     */
+    default void pipe(final @NonNull DataSink dataSink) {
+        pipe(dataSink, 16*1024);
+    }
 
     // -- FACTORIES
 
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/metamodel/_ObjectGraphFactory.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/metamodel/_ObjectGraphFactory.java
index 008a1a522c..c4744a05f1 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/metamodel/_ObjectGraphFactory.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/metamodel/_ObjectGraphFactory.java
@@ -119,12 +119,13 @@ class _ObjectGraphFactory implements ObjectGraph.Factory {
             final ObjectGraph.RelationType relationType,
             final String fromId,
             final String toId,
-            final String label) {
+            final String description) {
         val relation = new ObjectGraph.Relation(
                 relationType,
                 objectById.get(fromId),
                 objectById.get(toId),
-                label, "");
+                description,
+                "", "");
         objectGraph.relations().add(relation);
         return relation;
     }
@@ -136,12 +137,17 @@ class _ObjectGraphFactory implements ObjectGraph.Factory {
                 objSpec.isAbstract()
                     ? Optional.of("abstract")
                     : Optional.empty(),
+                Optional.ofNullable(objSpec.getDescription()),
                 new ArrayList<>());
         return obj;
     }
 
     private ObjectGraph.Field fieldForAss(final ObjectAssociation ass) {
-        return new ObjectGraph.Field(ass.getId(), 
objectShortName(ass.getElementType()), ass.isOneToManyAssociation());
+        return new ObjectGraph.Field(
+                ass.getId(),
+                objectShortName(ass.getElementType()),
+                ass.isOneToManyAssociation(),
+                ass.getStaticDescription());
     }
 
     private Set<Relation> createInheritanceRelations() {
@@ -162,7 +168,7 @@ class _ObjectGraphFactory implements ObjectGraph.Factory {
                     // we found an inheritance relation
                     val relation = new ObjectGraph.Relation(
                             ObjectGraph.RelationType.INHERITANCE,
-                            objectById.get(o1.id()), objectById.get(o2.id()), 
"", "");
+                            objectById.get(o1.id()), objectById.get(o2.id()), 
"", "", "");
                     inheritanceRelations.add(relation);
                 }
             }
diff --git 
a/valuetypes/asciidoc/builder/src/main/java/org/apache/causeway/valuetypes/asciidoc/builder/objgraph/d3js/ObjectGraphRendererD3js.java
 
b/valuetypes/asciidoc/builder/src/main/java/org/apache/causeway/valuetypes/asciidoc/builder/objgraph/d3js/ObjectGraphRendererD3js.java
index fbe744fd37..d285f5deae 100644
--- 
a/valuetypes/asciidoc/builder/src/main/java/org/apache/causeway/valuetypes/asciidoc/builder/objgraph/d3js/ObjectGraphRendererD3js.java
+++ 
b/valuetypes/asciidoc/builder/src/main/java/org/apache/causeway/valuetypes/asciidoc/builder/objgraph/d3js/ObjectGraphRendererD3js.java
@@ -102,7 +102,7 @@ public class ObjectGraphRendererD3js implements 
ObjectGraph.Renderer {
         objGraph.relations().forEach(rel->{
             val source = objectLookup.get(rel.from());
             val target = objectLookup.get(rel.to());
-            d3jsGraph.links.add(new D3jsGraph.Link(source, target, 
rel.labelFormatted()));
+            d3jsGraph.links.add(new D3jsGraph.Link(source, target, 
rel.multiplicityNotation().apply(rel.description())));
         });
 
         renderSvg(sb, d3jsGraph);
diff --git 
a/valuetypes/asciidoc/builder/src/main/java/org/apache/causeway/valuetypes/asciidoc/builder/objgraph/plantuml/ObjectGraphRendererPlantuml.java
 
b/valuetypes/asciidoc/builder/src/main/java/org/apache/causeway/valuetypes/asciidoc/builder/objgraph/plantuml/ObjectGraphRendererPlantuml.java
index 32253c842f..b1c60e0e37 100644
--- 
a/valuetypes/asciidoc/builder/src/main/java/org/apache/causeway/valuetypes/asciidoc/builder/objgraph/plantuml/ObjectGraphRendererPlantuml.java
+++ 
b/valuetypes/asciidoc/builder/src/main/java/org/apache/causeway/valuetypes/asciidoc/builder/objgraph/plantuml/ObjectGraphRendererPlantuml.java
@@ -78,9 +78,10 @@ public class ObjectGraphRendererPlantuml implements 
ObjectGraph.Renderer {
         case ONE_TO_ONE:
         case ONE_TO_MANY:
         case MERGED_ASSOCIATIONS:
-            return String.format("%s -> \"%s\" %s", rel.fromId(), 
rel.labelFormatted(), rel.toId());
+            return String.format("%s -> \"%s\" %s", rel.fromId(), 
rel.description(), rel.toId());
         case BIDIR_ASSOCIATION:
-            return String.format("%s \"%s\" -- \"%s\" %s", rel.fromId(), 
rel.label(), rel.label2(), rel.toId());
+            return String.format("%s \"%s\" -- \"%s\" %s : %s",
+                    rel.fromId(), rel.nearLabel(), rel.farLabel(), rel.toId(), 
rel.description());
         case INHERITANCE:
             return String.format("%s --|> %s", rel.fromId(), rel.toId());
         }

Reply via email to