package org.eclipse.uml2.diagram.sequence.internal.layout.vertical;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.uml2.diagram.sequence.internal.layout.vertical.input.HorizontalConstraint;
import org.eclipse.uml2.diagram.sequence.internal.layout.vertical.input.LifeLine;
import org.eclipse.uml2.diagram.sequence.internal.layout.vertical.input.LifeLineElement;
import org.eclipse.uml2.diagram.sequence.internal.layout.vertical.input.OrderingConstraint;
import org.eclipse.uml2.diagram.sequence.internal.layout.vertical.state.LifelineState;
import org.eclipse.uml2.diagram.sequence.internal.layout.vertical.state.St1MinPosIsCalculated;
import org.eclipse.uml2.diagram.sequence.internal.layout.vertical.state.St2BeforeConstraintsAccounted;
import org.eclipse.uml2.diagram.sequence.internal.layout.vertical.state.St3PositionIsSet;
import org.eclipse.uml2.diagram.sequence.internal.layout.vertical.state.St9LifelineFinished;
public class NewLayoutSession {
private static final boolean debug_print = false;
/**
* @throws IllegalArgumentException if lifelines is empty
*
*/
public NewLayoutSession(LifeLine [] lifeLines, boolean fullLayout, int topPos) {
if (lifeLines.length == 0) {
throw new IllegalArgumentException("No lifelines are specified"); //$NON-NLS-1$
}
if (false) {
new InputAsserter(lifeLines);
}
myLifeline2State = new LinkedHashMap(lifeLines.length);
for (int i=0; i<lifeLines.length; i++) {
LifeLine lifeLine = lifeLines[i];
LifelineState lifelineState = new LifelineState(lifeLine, topPos, fullLayout, myBrokenOrderingConstraints);
myLifeline2State.put(lifeLine, lifelineState);
}
}
public int go() {
if (debug_print) {
System.out.println("\n\n"); //$NON-NLS-1$
System.out.println("[NewLayoutSession.go] >>> "+myLifeline2State.size()+" lifelines"); //$NON-NLS-1$ //$NON-NLS-2$
}
try {
for (Iterator it = myLifeline2State.values().iterator(); it.hasNext(); ) {
LifelineState lifelineState = (LifelineState) it.next();
if (lifelineState.getState() != St9LifelineFinished.class) {
processLifeline(lifelineState);
}
}
for (Iterator it = myLifeline2State.values().iterator(); it.hasNext(); ) {
LifelineState lifelineState = (LifelineState) it.next();
lifelineState.savePositions();
}
LifelineState firstLifelineState = (LifelineState) myLifeline2State.values().iterator().next();
return St9LifelineFinished.getBase(firstLifelineState);
} catch (RuntimeException e) {
if (debug_print) {
new Exception("Exception caught", e).printStackTrace(System.out); //$NON-NLS-1$
}
throw e;
} finally {
if (debug_print) {
System.out.println("[NewLayoutSession.go] <<<"); //$NON-NLS-1$
System.out.println("\n\n\n"); //$NON-NLS-1$
}
}
}
void processLifeline(LifelineState lifelineState) {
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] >>> lifelineState="+lifelineState); //$NON-NLS-1$
}
try {
boolean available = myCycleWatcher.requireLifeline(lifelineState);
if (!available) {
throw new IllegalStateException("Lifeline is locked somehow"); //$NON-NLS-1$
}
try {
processLifelineAlreadyLocked(lifelineState, NOT_REQUIRED_CONSTRAINT);
} finally {
myCycleWatcher.releaseLifeline(lifelineState);
}
} finally {
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] <<<"); //$NON-NLS-1$
}
}
}
/**
* @return pos of top element
*/
int processLifeline(LifelineState lifelineState, OrderingConstraint requiredConstraint) throws LifelineBlockedException {
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] >>> lifelineState="+lifelineState+" requiredConstraint="+requiredConstraint); //$NON-NLS-1$ //$NON-NLS-2$
}
try {
Integer pos = (Integer) lifelineState.getEncounteredOrderingConstraints2Pos().remove(requiredConstraint);
if (pos != null) {
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] required constraint found in encountered after constraints"); //$NON-NLS-1$
}
return pos.intValue();
}
lockAndProcessLifeline(lifelineState, requiredConstraint);
myCycleWatcher.releaseLifeline(lifelineState);
pos = (Integer) lifelineState.getEncounteredOrderingConstraints2Pos().remove(requiredConstraint);
if (pos == null) {
throw new RuntimeException("Failed to get position of top element"); //$NON-NLS-1$
}
return pos.intValue();
} finally {
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] <<<"); //$NON-NLS-1$
}
}
}
void processLifeline(LifelineState lifelineState, HorizontalConstraint requiredConstraint) throws LifelineBlockedException {
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] >>> lifelineState="+lifelineState+" requiredConstraint="+requiredConstraint); //$NON-NLS-1$ //$NON-NLS-2$
}
try {
lockAndProcessLifeline(lifelineState, requiredConstraint);
} finally {
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] <<<"); //$NON-NLS-1$
}
}
}
void lockAndProcessLifeline(LifelineState lifelineState, Object requiredConstraint) throws LifelineBlockedException {
boolean available = myCycleWatcher.requireLifeline(lifelineState);
if (!available) {
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] lifeline is blocked"); //$NON-NLS-1$
}
throw new LifelineBlockedException();
}
processLifelineAlreadyLocked(lifelineState, requiredConstraint);
}
/*
* States per element:
* 1. minPos is calculated
* 2. "before ordering" position constraint is calculated
* 3. pos is set according to horizontal constraint
* 4.(hidden) encountered "after ordering" are accounted
*
* 5. lifeline is finished
*
* @return in St2BeforeConstraintsAccounted, St9LifelineFinished or St3PositionIsSet if for OrderingConstraint
*/
void processLifelineAlreadyLocked(LifelineState lifelineState, Object requiredConstraint) {
if (lifelineState.getState() == St3PositionIsSet.class) {
if (debug_print) {
System.out.println("[NewLayoutSession.processLifelineAlreadyLocked] current element is "+lifelineState.getStateStringDebug()); //$NON-NLS-1$
System.out.println("[NewLayoutSession.processLifelineAlreadyLocked] position is already set"); //$NON-NLS-1$
}
St3PositionIsSet.nextStep(lifelineState);
}
while (lifelineState.getState() != St9LifelineFinished.class) {
// must be in MinPosIsCalculated
if (debug_print) {
System.out.println("[NewLayoutSession.processLifelineAlreadyLocked] current element is "+lifelineState.getStateStringDebug()); //$NON-NLS-1$
}
int elementMinPos = St1MinPosIsCalculated.getMinPos(lifelineState);
for (Iterator beforeConstraintIt = St1MinPosIsCalculated.getRequiredOrderingConstraints(lifelineState); beforeConstraintIt.hasNext(); ) {
OrderingConstraint beforeConstraint = (OrderingConstraint) beforeConstraintIt.next();
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] beforeConstraint="+beforeConstraint); //$NON-NLS-1$
}
LifelineState otherLifelineState = getLifelineState(beforeConstraint.getBeforeElement().getLifeLine());
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] otherLifelineState="+otherLifelineState); //$NON-NLS-1$
}
try {
int otherPos = processLifeline(otherLifelineState, beforeConstraint);
beforeConstraint.setInvalid(false);
int resultMinPos = otherPos + beforeConstraint.getMinSlopeValue();
elementMinPos = Math.max(elementMinPos, resultMinPos);
} catch (LifelineBlockedException e) {
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] lifeline is not available"); //$NON-NLS-1$
}
beforeConstraint.setInvalid(true);
myBrokenOrderingConstraints.add(beforeConstraint);
}
}
St1MinPosIsCalculated.beforeConstraintsAccounted(lifelineState, elementMinPos);
// in BeforeConstraintsAccounted state
HorizontalConstraint horizontalConstraint = St2BeforeConstraintsAccounted.getHorizontalConstraint(lifelineState);
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] horizontalConstraint="+horizontalConstraint); //$NON-NLS-1$
}
if (horizontalConstraint == requiredConstraint) {
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] required constraint found"); //$NON-NLS-1$
}
return;
}
if (horizontalConstraint == null) {
elementMinPos = Math.max(elementMinPos, St2BeforeConstraintsAccounted.getCurrentPos(lifelineState));
} else {
Set badElementsSet = (Set) myBlockedHorizontal2BadElements.get(horizontalConstraint);
if (badElementsSet == null) {
badElementsSet = new HashSet(0);
myBlockedHorizontal2BadElements.put(horizontalConstraint, badElementsSet);
try {
List elements = horizontalConstraint.getLifeLineElementsList();
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] constrained elements "+elements); //$NON-NLS-1$
}
boolean prioritedCoordinateFound = false;
int constraintMinPos = Integer.MIN_VALUE;
int constraintCurPos = Integer.MIN_VALUE;
List resolvedOtherLifelines = new ArrayList(elements.size());
for (Iterator it = elements.iterator(); it.hasNext(); ) {
LifeLineElement element = (LifeLineElement) it.next();
LifelineState otherLifelineState = getLifelineState(element.getLifeLine());
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] element="+element+", on lifeline="+otherLifelineState); //$NON-NLS-1$ //$NON-NLS-2$
}
if (lifelineState.getCurrentElement() == element) {
horizontalConstraint.elementIsResolved(element);
} else if (badElementsSet.contains(element)) {
horizontalConstraint.elementIsViolated(element);
continue;
} else {
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] non-current"); //$NON-NLS-1$
}
try {
processLifeline(otherLifelineState, horizontalConstraint);
} catch (LifelineBlockedException e) {
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] lifeline is blocked"); //$NON-NLS-1$
}
horizontalConstraint.elementIsViolated(element);
otherLifelineState.addBrokenHorizontalConstraints(horizontalConstraint);
continue;
}
horizontalConstraint.elementIsResolved(element);
resolvedOtherLifelines.add(otherLifelineState);
}
int minPos = St2BeforeConstraintsAccounted.getBeforeConstraintPos(otherLifelineState);
int curPos = St2BeforeConstraintsAccounted.getCurrentPos(otherLifelineState);
if (constraintMinPos < minPos) {
constraintMinPos = minPos;
}
if (!prioritedCoordinateFound) {
constraintCurPos = curPos;
prioritedCoordinateFound = St2BeforeConstraintsAccounted.isPrioritedPosition(otherLifelineState);
}
}
int constraintPos = Math.max(constraintCurPos, constraintMinPos);
for (int i=0; i<resolvedOtherLifelines.size(); i++) {
LifelineState state = (LifelineState) resolvedOtherLifelines.get(i);
St2BeforeConstraintsAccounted.setPos(state, constraintPos);
myCycleWatcher.releaseLifeline(state);
}
elementMinPos = constraintPos;
} finally {
myBlockedHorizontal2BadElements.remove(horizontalConstraint);
}
} else {
badElementsSet.add(lifelineState.getCurrentElement());
}
}
St2BeforeConstraintsAccounted.setPos(lifelineState, elementMinPos);
// now in St3PositionIsSet state
if (lifelineState.getEncounteredOrderingConstraints2Pos().containsKey(requiredConstraint)) {
return;
}
St3PositionIsSet.nextStep(lifelineState);
}
if (debug_print) {
System.out.println("[NewLayoutSession.processLifeline] lifeline is finished"); //$NON-NLS-1$
}
if (requiredConstraint != NOT_REQUIRED_CONSTRAINT) {
throw new RuntimeException("Failed to find required constraint "+requiredConstraint+" on lifeline "+lifelineState); //$NON-NLS-1$ //$NON-NLS-2$
}
}
private static class LifelineBlockedException extends Exception {
}
private LifelineState getLifelineState(LifeLine lifeLine) {
LifelineState result = (LifelineState) myLifeline2State.get(lifeLine);
if (result == null) {
throw new RuntimeException("Failed to find state for lifeline"); //$NON-NLS-1$
}
return result;
}
private final Map myLifeline2State;
private final CycleWatcher myCycleWatcher = new CycleWatcher();
private final Set myBrokenOrderingConstraints = new HashSet(2);
private final Map myBlockedHorizontal2BadElements = new HashMap(3);
private static final Object NOT_REQUIRED_CONSTRAINT = new Object();
private static class CycleWatcher {
boolean requireLifeline(LifelineState lifelineState) {
return myLifelineStates.add(lifelineState);
}
void releaseLifeline(LifelineState lifelineState) {
myLifelineStates.remove(lifelineState);
}
private final HashSet myLifelineStates = new HashSet();
}
}