package com.baselet.element.sequence_aio.facet; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import java.util.PriorityQueue; import java.util.Queue; import com.baselet.element.sequence_aio.facet.LifelineSpanningTickSpanningOccurrence.ContainerPadding; import com.baselet.element.sequence_aio.facet.SequenceDiagram.DoubleConverter; public class HorizontalDrawingInfoImpl implements HorizontalDrawingInfo { private final double diagramStart; private final double diagramWidth; private final LifelineHorizontalDrawingInfo[] horizontalDrawingInfos; private final Map<Container, Double> containerLeftPadding; private final Map<Container, Double> containerRightPadding; /** * Calculates the actual lifeline and diagram width. * @param diagramStart left most point were the diagram can draw * @param diagramMinWidth * @param widthConverter used to adjust the calculated width (should only increase the width!) * @param lifelineWidth width of a single lifeline without any paddings * @param lifelineXPadding padding between two lifelines or a lifeline and the diagram border * @param lifelineCount * @param lastTick * @param paddings */ public HorizontalDrawingInfoImpl(double diagramStart, double diagramMinWidth, DoubleConverter widthConverter, double lifelineWidth, double lifelineXPadding, int lifelineCount, int lastTick, Collection<ContainerPadding> paddings) { containerLeftPadding = new HashMap<Container, Double>((int) (paddings.size() / 0.7)); containerRightPadding = new HashMap<Container, Double>((int) (paddings.size() / 0.7)); horizontalDrawingInfos = new LifelineHorizontalDrawingInfo[lifelineCount]; this.diagramStart = diagramStart; // calculate the padding information and the maximum padding double maxPadding = 0; double[][] leftPaddings = new double[lifelineCount][]; double[][] rightPaddings = new double[lifelineCount][]; for (int i = 0; i < lifelineCount; i++) { PaddingInfo paddingInfo = getPaddings(i, true, lastTick, paddings); leftPaddings[i] = paddingInfo.paddings; maxPadding = Math.max(maxPadding, paddingInfo.maxPadding); paddingInfo = getPaddings(i, false, lastTick, paddings); rightPaddings[i] = paddingInfo.paddings; maxPadding = Math.max(maxPadding, paddingInfo.maxPadding); } lifelineWidth += maxPadding * 2; // add the padding because it is not included in the lifeline width double diagramWidth = lifelineWidth * lifelineCount + lifelineXPadding * (lifelineCount + 1); if (diagramWidth < diagramMinWidth) { diagramWidth = diagramMinWidth; if (lifelineCount > 0) { lifelineWidth = (diagramWidth - lifelineXPadding * (lifelineCount + 1)) / lifelineCount; } } // adjust the width with the width converter diagramWidth = widthConverter.convert(diagramWidth); double lifelineHeadLeftStart = (diagramWidth - (lifelineWidth * lifelineCount + lifelineXPadding * (lifelineCount - 1)) ) / 2.0; this.diagramWidth = diagramWidth; for (int i = 0; i < horizontalDrawingInfos.length; i++) { horizontalDrawingInfos[i] = new LifelineHorizontalDrawingInfoImpl( leftPaddings[i], rightPaddings[i], lifelineHeadLeftStart + (lifelineWidth + lifelineXPadding) * i, lifelineHeadLeftStart + (lifelineWidth + lifelineXPadding) * i + lifelineWidth); } } private PaddingInfo getPaddings(int lifelineId, boolean left, int lastTick, Collection<ContainerPadding> allPaddings) { double[] lifelinePaddings = new double[lastTick + 2]; // define 1 queue for starting of padding intervals, and the other for the ending of the intervals Queue<ContainerPadding> paddingQueueStart = new PriorityQueue<ContainerPadding>(5, ContainerPadding.getContainerStartTickLifelineAscComparator()); LinkedList<ContainerPadding> paddingListEnd = new LinkedList<ContainerPadding>(); for (ContainerPadding cp : allPaddings) { if (left && cp.getContainer().getFirstLifeline().getIndex() == lifelineId && cp.getLeftPadding() > 0) { paddingQueueStart.add(cp); } else if (!left && cp.getContainer().getLastLifeline().getIndex() == lifelineId && cp.getRightPadding() > 0) { paddingQueueStart.add(cp); } } double maxPadding = 0; for (int tick = 0; tick < lifelinePaddings.length; tick++) { // insert paddings that start at the current tick at the right place (endTick asc) and remove them. while (paddingQueueStart.peek() != null && paddingQueueStart.peek().getContainer().getStartTick() == tick) { paddingListEnd.addLast(paddingQueueStart.poll()); } Iterator<ContainerPadding> endIter = paddingListEnd.iterator(); while (endIter.hasNext()) { ContainerPadding paddingInterval = endIter.next(); if (left) { if (!containerLeftPadding.containsKey(paddingInterval.getContainer())) { containerLeftPadding.put(paddingInterval.getContainer(), lifelinePaddings[tick]); } lifelinePaddings[tick] += paddingInterval.getLeftPadding(); } else { if (!containerRightPadding.containsKey(paddingInterval.getContainer())) { containerRightPadding.put(paddingInterval.getContainer(), lifelinePaddings[tick]); } lifelinePaddings[tick] += paddingInterval.getRightPadding(); } if (paddingInterval.getContainer().getEndTick() == tick) { endIter.remove(); } } maxPadding = Math.max(maxPadding, lifelinePaddings[tick]); } return new PaddingInfo(lifelinePaddings, maxPadding); } @Override public LifelineHorizontalDrawingInfo getHDrawingInfo(Lifeline lifeline) { return horizontalDrawingInfos[lifeline.getIndex()]; } @Override public double getSymmetricWidth(Lifeline ll1, Lifeline ll2, int tick) { if (ll1.getIndex() <= ll2.getIndex()) { return getHDrawingInfo(ll2).getSymmetricHorizontalEnd(tick) - getHDrawingInfo(ll1).getSymmetricHorizontalStart(tick); } else { return getHDrawingInfo(ll1).getSymmetricHorizontalEnd(tick) - getHDrawingInfo(ll2).getSymmetricHorizontalStart(tick); } } @Override public double getHorizontalStart(Container container) { return getHDrawingInfo(container.getFirstLifeline()).getHorizontalStart() + containerLeftPadding.get(container); } @Override public double getHorizontalEnd(Container container) { return getHDrawingInfo(container.getLastLifeline()).getHorizontalEnd() - containerRightPadding.get(container); } @Override public double getWidth(Container container) { return getHorizontalEnd(container) - getHorizontalStart(container); } @Override public double getDiagramHorizontalStart() { return diagramStart; } @Override public double getDiagramHorizontalEnd() { return diagramStart + diagramWidth; } @Override public double getDiagramWidth() { return diagramWidth; } private static class PaddingInfo { private final double[] paddings; private final double maxPadding; public PaddingInfo(double[] paddings, double maxPadding) { super(); this.paddings = paddings; this.maxPadding = maxPadding; } } }