You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/03/26 14:02:35 UTC
[isis] branch master updated: ISIS-2573: xray: add simple lifeline
support
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/isis.git
The following commit(s) were added to refs/heads/master by this push:
new eddd1f2 ISIS-2573: xray: add simple lifeline support
eddd1f2 is described below
commit eddd1f2cd704791226abcccdde60d7c007a4c35e
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Mar 26 15:02:23 2021 +0100
ISIS-2573: xray: add simple lifeline support
---
.../debug/xray/sequence/SequenceDiagram.java | 142 +++++++++++++++++++--
.../commons/internal/base/debug/XrayUiTest.java | 6 +-
2 files changed, 133 insertions(+), 15 deletions(-)
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/sequence/SequenceDiagram.java b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/sequence/SequenceDiagram.java
index df9b579..a7746bd 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/sequence/SequenceDiagram.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/debug/xray/sequence/SequenceDiagram.java
@@ -29,9 +29,11 @@ import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
+import org.apache.isis.commons.collections.Can;
import org.apache.isis.commons.internal.base._Refs;
import org.apache.isis.commons.internal.base._Refs.IntReference;
import org.apache.isis.commons.internal.debug.xray.sequence._Graphics.TextBlock;
+import org.apache.isis.commons.internal.primitives._Ints;
import lombok.Getter;
import lombok.NonNull;
@@ -44,6 +46,7 @@ public class SequenceDiagram {
private final Map<String, Participant> participantsById = new LinkedHashMap<>();
private final List<Connection> connections = new ArrayList<>();
+ private final List<Lifeline> lifelines = new ArrayList<>();
private Dimension size;
@@ -55,13 +58,13 @@ public class SequenceDiagram {
public void enter(final @NonNull String from, final @NonNull String to, String label) {
val p0 = participantsById.computeIfAbsent(from, id->new Participant(aliases.getOrDefault(id, id)));
val p1 = participantsById.computeIfAbsent(to, id->new Participant(aliases.getOrDefault(id, id)));
- connections.add(new Connection(p0, p1, label, false));
+ connections.add(new Connection(connections.size(), p0, p1, label, false));
}
public void exit(final @NonNull String from, final @NonNull String to, String label) {
val p1 = participantsById.computeIfAbsent(to, id->new Participant(aliases.getOrDefault(id, id)));
val p0 = participantsById.computeIfAbsent(from, id->new Participant(aliases.getOrDefault(id, id)));
- connections.add(new Connection(p0, p1, label, true));
+ connections.add(new Connection(connections.size(), p0, p1, label, true));
}
public void enter(String from, String to) {
@@ -71,6 +74,23 @@ public class SequenceDiagram {
public void exit(String from, String to) {
exit(from, to, null);
}
+
+ public void activate(String participantId) {
+ val participant = participantsById
+ .computeIfAbsent(participantId, id->new Participant(aliases.getOrDefault(id, id)));
+ val latestConnection = Can.ofCollection(connections).getLast().orElse(null);
+ lifelines.add(new Lifeline(participant, latestConnection));
+ }
+
+ public void deactivate(String participantId) {
+ val participant = participantsById
+ .computeIfAbsent(participantId, id->new Participant(aliases.getOrDefault(id, id)));
+ val latestConnection = Can.ofCollection(connections).getLast().orElse(null);
+ Can.ofCollection(lifelines).reverse().stream()
+ .filter(lifeline->lifeline.getParticipant().equals(participant))
+ .findFirst()
+ .ifPresent(lifeline->lifeline.endAt = latestConnection);
+ }
// -- RENDERING
@@ -84,6 +104,9 @@ public class SequenceDiagram {
private final static int PARTICIPANT_MAX_CHAR_PER_LINE = 26;
private final static Optional<Font> PARTICIPANT_FONT = _Graphics.lookupFont("Verdana", 12.f);
+ private final static Color LIFELINE_BACKGROUND_COLOR = Color.WHITE;
+ private final static int LIFELINE_WIDTH = 8;
+
private final static int CONNECTION_MARGIN_V = 12;
private final static int CONNECTION_LABEL_PADDING_H = 8;
private final static int CONNECTION_LABEL_PADDING_V = 3;
@@ -92,6 +115,7 @@ public class SequenceDiagram {
@Getter @RequiredArgsConstructor
private static class Connection {
+ final int index;
final Participant from;
final Participant to;
final String label;
@@ -99,15 +123,43 @@ public class SequenceDiagram {
TextBlock textBlock;
+ int x_left;
+ int x_from;
+ int x_to;
+
int y_top;
int y_bottom;
int height;
- void layout(Graphics2D g, IntReference y_offset) {
+ void layout(Graphics2D g, IntReference y_offset, List<Lifeline> lifelines) {
+
+ x_from = from.getX_middle();
+ x_to = to.getX_middle();
+
+ val fromConnectsLifeline = lifelines.stream()
+ .filter(ll->ll.getParticipant().equals(from))
+ .anyMatch(ll->ll.overlaps(this));
+
+ val toConnectsLifeline = lifelines.stream()
+ .filter(ll->ll.getParticipant().equals(to))
+ .anyMatch(ll->ll.overlaps(this));
+
+ final int dir = from.getX_middle() < to.getX_middle()
+ ? 1
+ : -1;
+ if(fromConnectsLifeline) {
+ x_from+= dir * LIFELINE_WIDTH / 2;
+ }
+ if(toConnectsLifeline) {
+ x_to-= dir * LIFELINE_WIDTH / 2;
+ }
+
+ x_left = Math.min(x_from, x_to);
+
y_top = y_offset.getValue() + CONNECTION_MARGIN_V;
-
+
textBlock = new TextBlock(label,
- Math.min(from.getX_middle(), to.getX_middle()),
+ x_left,
y_top);
val dim = textBlock.layout(g.getFontMetrics(),
@@ -160,6 +212,48 @@ public class SequenceDiagram {
x_offset.update(x->x + width + PARTICIPANT_MARGIN_H);
}
}
+
+ @Getter @RequiredArgsConstructor
+ private static class Lifeline {
+ final @NonNull Participant participant;
+ final Connection startAt;
+ Connection endAt;
+
+ int x_left;
+ int x_right;
+ int width;
+
+ int y_top;
+ int y_bottom;
+ int height;
+
+ void layout(Graphics2D g, int min_y, int max_y) {
+
+ width = LIFELINE_WIDTH;
+ x_left = participant.getX_middle() - LIFELINE_WIDTH / 2;
+ x_right = x_left + width;
+
+ y_top = startAt !=null
+ ? startAt.y_bottom
+ : min_y;
+ y_bottom = endAt !=null
+ ? endAt.y_bottom
+ : max_y;
+
+ height = y_bottom - y_top;
+ }
+
+ public boolean overlaps(Connection connection) {
+ val lowerBound = _Ints.Bound.inclusive(startAt != null
+ ? startAt.index
+ : -1);
+ val upperBound = _Ints.Bound.inclusive(endAt !=null
+ ? endAt.index
+ : Integer.MAX_VALUE);
+ return _Ints.Range.of(lowerBound, upperBound).contains(connection.index);
+ }
+ }
+
public Dimension layout(Graphics2D g) {
@@ -167,22 +261,31 @@ public class SequenceDiagram {
val x_offset = _Refs.intRef(PARTICIPANT_MARGIN_H);
val y_offset = _Refs.intRef(0);
+
participantsById.values().stream()
.peek(p->p.layout(g, x_offset))
.forEach(p->y_offset.update(x->Math.max(x, p.getHeight())));
-
+
final int width = x_offset.getValue();
y_offset.update(x->x + PARTICIPANT_MARGIN_V);
- CONNECTION_FONT.ifPresent(g::setFont);
+ final int y_offset_first_con = y_offset.getValue();
+ CONNECTION_FONT.ifPresent(g::setFont);
+
connections.stream()
- .forEach(c->c.layout(g, y_offset));
+ .forEach(c->c.layout(g, y_offset, lifelines));
+ final int y_offset_last_con = y_offset.getValue();
+
final int height = y_offset.update(x->x + 2*PARTICIPANT_MARGIN_V);
-
- return this.size = new Dimension(width, height);
+ this.size = new Dimension(width, height);
+
+ lifelines.stream()
+ .forEach(ll->ll.layout(g, y_offset_first_con - 3, y_offset_last_con + 3));
+
+ return this.size;
}
public void render(Graphics2D g) {
@@ -216,6 +319,19 @@ public class SequenceDiagram {
g.drawLine(p.getX_middle(), p.getY_bottom(), p.getX_middle(), size.height - PARTICIPANT_MARGIN_V);
});
+
+ g.setStroke(_Graphics.STROKE_DEFAULT);
+
+ lifelines.stream()
+ .forEach(ll->{
+
+ g.setColor(LIFELINE_BACKGROUND_COLOR);
+ g.fillRect(ll.getX_left(), ll.getY_top(), ll.getWidth(), ll.getHeight());
+
+ g.setColor(PARTICIPANT_BORDER_COLOR);
+ g.drawRect(ll.getX_left(), ll.getY_top(), ll.getWidth(), ll.getHeight());
+ });
+
CONNECTION_FONT.ifPresent(g::setFont);
connections.stream()
@@ -227,11 +343,11 @@ public class SequenceDiagram {
g.setStroke(c.isDashedLine()
? _Graphics.STROKE_DASHED
- : _Graphics.STROKE_DEFAULT);
+ : _Graphics.STROKE_DEFAULT);
_Graphics.arrowHorizontal(g,
- c.getFrom().getX_middle(),
- c.getTo().getX_middle(),
+ c.getX_from(),
+ c.getX_to(),
c.getY_bottom());
// connection label
diff --git a/commons/src/test/java/org/apache/isis/commons/internal/base/debug/XrayUiTest.java b/commons/src/test/java/org/apache/isis/commons/internal/base/debug/XrayUiTest.java
index 42715a8..cd43a8f 100644
--- a/commons/src/test/java/org/apache/isis/commons/internal/base/debug/XrayUiTest.java
+++ b/commons/src/test/java/org/apache/isis/commons/internal/base/debug/XrayUiTest.java
@@ -52,13 +52,15 @@ class XrayUiTest {
sequenceData.alias("ex", "Execution\n- act\n- prop\n- coll");
sequenceData.enter("thread", "test");
- sequenceData.enter("test", "ix", "run anonymous");
+ sequenceData.enter("test", "ix", "run anonymous");
+ sequenceData.activate("ix");
sequenceData.enter("ix", "tx", "require NEW");
sequenceData.enter("ix", "ex", "execute");
sequenceData.exit("ex", "ix");
sequenceData.exit("tx", "ix", "exit\n(after commit/rollback/unknown)");
- sequenceData.exit("ix", "test", "exit");
+ sequenceData.exit("ix", "test", "exit");
+ sequenceData.deactivate("ix");
sequenceData.exit("test", "thread", "exit");
model.addContainerNode(root, "Container");