package org.eclipse.uml2.diagram.sequence.internal.layout.manage; import java.awt.Dimension; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.uml2.diagram.sequence.edit.parts.InteractionEditPart; import org.eclipse.uml2.diagram.sequence.internal.layout.GeometryConstants; import org.eclipse.uml2.diagram.sequence.internal.layout.abstractgde.AbsElement; import org.eclipse.uml2.diagram.sequence.internal.layout.abstractgde.AbsElementPropertyAccess; import org.eclipse.uml2.diagram.sequence.internal.layout.abstractgde.AbsNode; import org.eclipse.uml2.diagram.sequence.internal.layout.abstractgde.gef.AbsDiagramGef; import org.eclipse.uml2.diagram.sequence.internal.layout.abstractgde.gef.AbsElementGef; import org.eclipse.uml2.diagram.sequence.internal.layout.abstractgde.gef.AbsNodeGef; import org.eclipse.uml2.diagram.sequence.internal.layout.alien.AlienElementFactoryImpl; import org.eclipse.uml2.diagram.sequence.internal.layout.alien.DummyAlienElementsLayout; import org.eclipse.uml2.diagram.sequence.internal.layout.alien.IAlienElementsLayout; import org.eclipse.uml2.diagram.sequence.internal.layout.horizontal.LifelineCoveringFramesCache; import org.eclipse.uml2.diagram.sequence.internal.layout.horizontal.SDHorizontalLayout; import org.eclipse.uml2.diagram.sequence.internal.layout.model.JustReshapedState; import org.eclipse.uml2.diagram.sequence.internal.layout.model.LmAlienElement; import org.eclipse.uml2.diagram.sequence.internal.layout.model.LmObjectsResolver; import org.eclipse.uml2.diagram.sequence.internal.layout.model.LmReshapable; import org.eclipse.uml2.diagram.sequence.internal.layout.model.SDLayoutModel; import org.eclipse.uml2.diagram.sequence.internal.layout.model.SdLayoutModelAccess; import org.eclipse.uml2.diagram.sequence.internal.layout.vertical.SDVerticalLayout; import org.eclipse.uml2.uml.Interaction; /** * */ public class SdLayoutAsInnerLayout implements LifelineMotionListener /*, NestedLayout*/ { public SdLayoutAsInnerLayout(InteractionEditPart modelNodeEditPart) { this(modelNodeEditPart, (Interaction)modelNodeEditPart.resolveSemanticElement()); } public SdLayoutAsInnerLayout(InteractionEditPart modelNodeEditPart, Interaction interactionEntity) { myAbsDiagramWithRootNode = new AbsDiagramGef(modelNodeEditPart); myInteractionEntity = interactionEntity; myInteractionView = modelNodeEditPart.getNotationView(); myEvents = new UpdateEventAccumulator(myAbsDiagramWithRootNode.getAbsElementFactory(), modelNodeEditPart); myLayoutModelHolder = new LayoutModelHolder(false); myHorizontalLayout = new SDHorizontalLayout(myLayoutModelHolder); SDVerticalLayout.Config verticalLayoutConfig = new SDVerticalLayout.Config() { }; myVerticalLayout = new SDVerticalLayout(myLayoutModelHolder, verticalLayoutConfig); //[U2T] in Arcas was: //myAlienElementsLayout = new AlienElementsLayout(myLayoutModelHolder, myAbsDiagramWithRootNode, new AbsLayoutAccessGef(modelNodeEditPart.getViewer())); myAlienElementsLayout = new DummyAlienElementsLayout(); } private void elementAdded(IGraphicalEditPart element) { myEvents.elementAdded(element); } private void elementRemoved(IGraphicalEditPart element) { myEvents.elementRemoved(element); } public void nodeAdded(GraphicalEditPart modelNodeEditPart) { elementAdded(modelNodeEditPart); } public void nodeRemoved(GraphicalEditPart modelNodeEditPart) { elementRemoved(modelNodeEditPart); } public void linkAdded(ConnectionEditPart modelLinkEditPart) { elementAdded(modelLinkEditPart); myInteractionLinksProvider.add(modelLinkEditPart); } public void linkRemoved(ConnectionEditPart modelLinkEditPart) { elementRemoved(modelLinkEditPart); myInteractionLinksProvider.remove(modelLinkEditPart); } public void elementReshaped(IGraphicalEditPart modelElementEditPart) { JustReshapedState justReshapedState = new JustReshapedState(myServer); AbsElement absElement = myAbsDiagramWithRootNode.getAbsElementFactory().createAbsElement(modelElementEditPart); myAlienElementProcessor.elementReshaped(absElement); LmObjectsResolver resolver = myLayoutModelHolder.getLayoutModel().getLmObjectsResolver(); Object lmObject = resolver.getLmObject(absElement); if (lmObject instanceof LmReshapable) { startAdditionalReshapingListening(); try { ((LmReshapable) lmObject).setJustReshaped(justReshapedState); } finally { stopAdditionalReshapingListening(); } } } public Collection performLayout() { Collection result = performLayout(false); result.addAll(myAdditionalReshapeElements); myAdditionalReshapeElements.clear(); return result; } public Collection performFullLayout() { if (!myAdditionalReshapeElements.isEmpty()) { throw new IllegalStateException(); } return performLayout(true); } public void lifelineMoved(int horizontalDelta, GraphicalEditPart lifelineEditPart) { LifelineCoveringFramesCache cache = myHorizontalLayout.getLifelineCoveringFramesCache(); if (cache == null) { assert false; return; } LifelineCoveringFramesCache.LifelineCoveringFramesMover mover = cache.getLifelineCoveringFramesMover(myAbsDiagramWithRootNode.getAbsElementFactory().createAbsNode(lifelineEditPart)); if (mover != null) { startAdditionalReshapingListening(); try { mover.lifelineMoved(horizontalDelta); } finally { stopAdditionalReshapingListening(); } } } public InteractionLinksProvider getInteractionLilnksProvider() { return myInteractionLinksProvider; } private Collection performLayout(boolean fullLayout) { Collection result; try { //myEvents.accept(myChecker); myEvents.accept(myLayoutModelHolder.getLayoutModel().newAddRemoveProcessor()); myEvents.accept(myAlienElementProcessor); result = performSdLayout(fullLayout); } finally { myEvents.clear(); myAlienElementProcessor = new AlienElementProcessor(); } myServer.incrementCode(); return result; } private static abstract class DebugPositionWatcherGate { abstract void positionIsChanged(AbsElementGef absElement, int coordCode); abstract void finish(); static final DebugPositionWatcherGate TRIVIAL = new DebugPositionWatcherGate() { void positionIsChanged(AbsElementGef absElement, int coordCode) {} void finish() {} }; } private static class DebugPositionWatcher extends DebugPositionWatcherGate { void positionIsChanged(AbsElementGef absElement, int coordCode) { validateAll(); if (coordCode == 0) { return; } final AbsNodeGef node = (AbsNodeGef) absElement; final PositionHolder holder; // initialize once switch (coordCode) { case AbsDiagramGef.ChangePositionWatcher.ChangePositionListener.X: holder = new PositionHolder() { int myX = node.getX(); void validate() { assert node.getX() == myX; } }; break; case AbsDiagramGef.ChangePositionWatcher.ChangePositionListener.Y: holder = new PositionHolder() { int myY = node.getY(); void validate() { assert node.getY() == myY; } }; break; case AbsDiagramGef.ChangePositionWatcher.ChangePositionListener.WIDTH: holder = new PositionHolder() { int myWidth = node.getWidth(); void validate() { if (node.getWidth() != myWidth) { throw new RuntimeException("Wrong width", getTrace()); //$NON-NLS-1$ } } }; break; case AbsDiagramGef.ChangePositionWatcher.ChangePositionListener.HEIGHT: holder = new PositionHolder() { int myHeight = node.getHeight(); void validate() { if (node.getHeight() != myHeight) { throw new RuntimeException("Wrong height", getTrace()); //$NON-NLS-1$ } } }; break; case AbsDiagramGef.ChangePositionWatcher.ChangePositionListener.ALL: return; default: throw new RuntimeException("Unknown coord code"); //$NON-NLS-1$ } myHolders.add(holder); holder.validate(); } void finish() { validateAll(); } private void validateAll() { for (Iterator it = myHolders.iterator(); it.hasNext(); ) { PositionHolder holder = (PositionHolder) it.next(); holder.validate(); } } private final List myHolders = new ArrayList(); abstract class PositionHolder { abstract void validate(); Exception getTrace() { return myTrace; } private final Exception myTrace = new Exception(); } } private Collection performSdLayout(boolean fullLayout) { final Collection result = new HashSet(); final DebugPositionWatcherGate positionWatcher = ourNeedAsserter ? new DebugPositionWatcher() : DebugPositionWatcherGate.TRIVIAL; AbsDiagramGef.ChangePositionWatcher.ChangePositionListener changePositionListener = new AbsDiagramGef.ChangePositionWatcher.ChangePositionListener() { public void positionIsChanged(AbsElementGef absElement, int coordCode) { result.add(absElement.getEditPart()); positionWatcher.positionIsChanged(absElement, coordCode); } }; myAbsDiagramWithRootNode.getChangePositionWatcher().setListener(changePositionListener); try { performSdLayoutImpl(fullLayout); } finally { myAbsDiagramWithRootNode.getChangePositionWatcher().setListener(null); } positionWatcher.finish(); assert assertLayoutDoesntMoveElementsNow(fullLayout); return result; } /** * Returns true or fail assert inside */ private boolean assertLayoutDoesntMoveElementsNow(boolean fullLayout) { AbsDiagramGef.ChangePositionWatcher.ChangePositionListener nonexpectingChangePositionListener = new AbsDiagramGef.ChangePositionWatcher.ChangePositionListener() { public void positionIsChanged(AbsElementGef absElement, int coordCode) { assert false : "Positions are different second time"; //$NON-NLS-1$ } }; myAbsDiagramWithRootNode.getChangePositionWatcher().setListener(nonexpectingChangePositionListener); try { performSdLayoutImpl(fullLayout); } finally { myAbsDiagramWithRootNode.getChangePositionWatcher().setListener(null); } return true; } private void performSdLayoutImpl(boolean fullLayout) { AbsNode interactionNode = myAbsDiagramWithRootNode.getInteractionAbsNode(); InteractionLayouter interactionLayouter = AbsElementPropertyAccess.getInstance().getInteractionLayouter(interactionNode); int pentagonHeight = interactionLayouter.getPentagonPreferredHeight(); int clientAreaTop = interactionNode.getY() + pentagonHeight; int clientAreaLeft = interactionNode.getX() + GeometryConstants.Interaction.LEFT_INSET; Dimension nativeElementsArea = performNativeLayoutImpl(clientAreaLeft, clientAreaTop, fullLayout); Dimension commonClientArea = performAlienLayoutImpl(clientAreaLeft, clientAreaTop, nativeElementsArea, fullLayout); if (interactionLayouter.isFullScreen()) { return; } int clientAreaWidth = commonClientArea.width; int clientAreaHeight = commonClientArea.height; clientAreaWidth = Math.max(clientAreaWidth, interactionLayouter.getPentagonPreferredWidth()); int interactionWidth = GeometryConstants.Interaction.LEFT_INSET + clientAreaWidth + GeometryConstants.Interaction.RIGHT_INSET ; int interactionHeight = pentagonHeight + clientAreaHeight + GeometryConstants.Interaction.BOTTOM_INSET; if (interactionNode.getWidth() < interactionWidth) { interactionNode.setWidth(interactionWidth); } if (interactionNode.getHeight() < interactionHeight) { interactionNode.setHeight(interactionHeight); } } private Dimension performNativeLayoutImpl(int clientAreaLeft, int clientAreaTop, boolean fullLayout) { int bottomPos = myVerticalLayout.doLayout(fullLayout, clientAreaTop); int rightPos = myHorizontalLayout.applyConstraints(clientAreaLeft, fullLayout); int width = rightPos - clientAreaLeft; int height = bottomPos - clientAreaTop; return new Dimension(width, height); } private Dimension performAlienLayoutImpl(int clientAreaLeft, int clientAreaTop, Dimension nativeElementsArea, boolean fullLayout) { Dimension alienElementsArea; if (fullLayout) { int alienElementsAreaLeft = clientAreaLeft + nativeElementsArea.width; int alienElementsAreaTop = clientAreaTop + GeometryConstants.Lifeline.SKIP_LIFELINE_FROM_TOP; alienElementsArea = myAlienElementsLayout.fullLayout(alienElementsAreaLeft, alienElementsAreaTop); alienElementsArea.width += nativeElementsArea.width; } else { Collection reshapedElements = myAlienElementProcessor.getReshapedGdeElements(); alienElementsArea = myAlienElementsLayout.layoutReshaped(reshapedElements, clientAreaLeft, clientAreaTop); } alienElementsArea.width = Math.max(alienElementsArea.width, nativeElementsArea.width); alienElementsArea.height = Math.max(alienElementsArea.height, nativeElementsArea.height); return alienElementsArea; } private static class InteractionLinksProviderImpl implements InteractionLinksProvider { public Iterator linkEditParts() { return myLinkEditParts.iterator(); } void add(ConnectionEditPart linkEditPart) { assert ! myLinkEditParts.contains(linkEditPart); myLinkEditParts.add(linkEditPart); } void remove(ConnectionEditPart linkEditPart) { assert myLinkEditParts.contains(linkEditPart); myLinkEditParts.remove(linkEditPart); } private final Set myLinkEditParts = new HashSet(); } private class LayoutModelHolder implements SdLayoutModelAccess { LayoutModelHolder(boolean readInitialState) { createNewModelImpl(readInitialState); } public SDLayoutModel getLayoutModel() { return myLayoutModel; } void createAnotherModel() { createNewModelImpl(true); } private void createNewModelImpl(boolean secondary) { SDLayoutModel.RootElementsGetter interactionEntityGetter = new SDLayoutModel.RootElementsGetter() { public Interaction getInteractionEntity() { return myInteractionEntity; } public View getInteractionView() { return myInteractionView; } public AbsNode getRootNode() { return myRootNode; } private final AbsNode myRootNode = myAbsDiagramWithRootNode.getInteractionAbsNode(); }; myLayoutModel = new SDLayoutModel(myAbsDiagramWithRootNode, secondary, myAlienElementfactory, new SDLayoutModel.Config() {}, interactionEntityGetter ); } private SDLayoutModel myLayoutModel; } private void startAdditionalReshapingListening() { myAbsDiagramWithRootNode.getChangePositionWatcher().setListener(myAdditionalReshapeListener); } private void stopAdditionalReshapingListening() { myAbsDiagramWithRootNode.getChangePositionWatcher().setListener(null); } private final InteractionLinksProviderImpl myInteractionLinksProvider = new InteractionLinksProviderImpl(); private final AbsDiagramGef myAbsDiagramWithRootNode; private final LmAlienElement.Factory myAlienElementfactory = new AlienElementFactoryImpl(); private final LayoutModelHolder myLayoutModelHolder; private final SDHorizontalLayout myHorizontalLayout; private final SDVerticalLayout myVerticalLayout; private final UpdateEventAccumulator myEvents; private final ReshapeServerImpl myServer = new ReshapeServerImpl(); private final Interaction myInteractionEntity; private final View myInteractionView; private AlienElementProcessor myAlienElementProcessor = new AlienElementProcessor(); private final IAlienElementsLayout myAlienElementsLayout; private final Collection myAdditionalReshapeElements = new HashSet(); private final AbsDiagramGef.ChangePositionWatcher.ChangePositionListener myAdditionalReshapeListener = new AbsDiagramGef.ChangePositionWatcher.ChangePositionListener() { public void positionIsChanged(AbsElementGef absElement, int coordCode) { myAdditionalReshapeElements.add(absElement.getEditPart()); } }; //private final ElementsAddRemoveOrderChecker myChecker = new ElementsAddRemoveOrderChecker(); static class ReshapeServerImpl implements JustReshapedState.Server { public int getCurrentReshapeCode() { return myReshapeCode; } void incrementCode() { myReshapeCode++; } private int myReshapeCode = 0; } private static final boolean ourNeedAsserter; static { boolean b = false; assert b = true; //b will be true, if assert executed ourNeedAsserter = b; } }