package org.eclipse.uml2.diagram.sequence.model.builder; import java.util.Iterator; import java.util.List; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDBehaviorSpec; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDBracketContainer; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDCombinedFragment; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDExecution; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDFactory; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDFrame; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDFrameContainer; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDGate; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDGateMessage; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDGateMessageEnd; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDInteractionOperand; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDInvocation; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDLifeLine; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDMessage; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDModel; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDMountingRegion; import org.eclipse.uml2.diagram.sequence.model.sequenced.SDSimpleNode; import org.eclipse.uml2.diagram.sequence.model.sequenced.impl.SDModelImpl; import org.eclipse.uml2.uml.ActionExecutionSpecification; import org.eclipse.uml2.uml.CombinedFragment; import org.eclipse.uml2.uml.Continuation; import org.eclipse.uml2.uml.ExecutionOccurrenceSpecification; import org.eclipse.uml2.uml.ExecutionSpecification; import org.eclipse.uml2.uml.Gate; import org.eclipse.uml2.uml.Interaction; import org.eclipse.uml2.uml.InteractionFragment; import org.eclipse.uml2.uml.InteractionOperand; import org.eclipse.uml2.uml.InteractionUse; import org.eclipse.uml2.uml.Lifeline; import org.eclipse.uml2.uml.Message; import org.eclipse.uml2.uml.MessageEnd; import org.eclipse.uml2.uml.MessageOccurrenceSpecification; import org.eclipse.uml2.uml.MessageSort; import org.eclipse.uml2.uml.StateInvariant; public class SDBuilder { private final Interaction myInteraction; private final StartAndFinishRegistry myStartsAndFinishes; private final LifeLineCallStack myCallStack; private final MessageNumbers myMessageNumbers; private SDModel mySDModel; private SDFrameContainer myCurrentFrameContainer; public SDBuilder(Interaction interaction) { myInteraction = interaction; myStartsAndFinishes = new StartAndFinishRegistry(myInteraction); myCallStack = new LifeLineCallStack(); myMessageNumbers = new MessageNumbers(this); } public Interaction getInteraction() { return myInteraction; } public void updateMessageNumbers() { myMessageNumbers.updateMessageNumbers(); } /** * For tests only */ public LifeLineCallStack getCallStack() { return myCallStack; } public SDModel getSDModel() { if (mySDModel == null) { reBuildModel(); } return mySDModel; } private SDModel reBuildModel() { myCallStack.clear(); myStartsAndFinishes.forceRemap(); mySDModel = SDFactory.eINSTANCE.createSDModel(); mySDModel.setUmlInteraction(myInteraction); myCurrentFrameContainer = mySDModel; /** * intentionally cast to implementation -- we don't want to allow clients to call this */ ((SDModelImpl) mySDModel).setUMLTracing(new SDBuilderTrace()); buildGates(mySDModel, myInteraction); buildLifeLines(mySDModel, myInteraction); for (Iterator<InteractionFragment> fragments = myInteraction.getFragments().iterator(); fragments.hasNext();) { buildBrackets(fragments); } updateMessageNumbers(); return mySDModel; } private void buildGates(SDModel model, Interaction interaction) { for (Gate umlGate : interaction.getFormalGates()) { SDGate sdGate = getTraceImpl().bindNewGate(umlGate); model.getGates().add(sdGate); } } private void buildLifeLines(SDModel model, Interaction interaction) { assert model.getLifelines().isEmpty(); for (Lifeline umlLifeline : interaction.getLifelines()) { SDLifeLine sdLifeLine = getTraceImpl().bindNewLifeline(umlLifeline); model.getLifelines().add(sdLifeLine); myCallStack.push(umlLifeline, sdLifeLine); } } private void buildBrackets(Iterator<InteractionFragment> orderedFragments) { if (!orderedFragments.hasNext()) { return; } InteractionFragment fragment = orderedFragments.next(); if (fragment instanceof StateInvariant) { buildSimpleNode(fragment); return; } if (fragment instanceof ActionExecutionSpecification) { unsupportedFragment(fragment); return; } if (fragment instanceof CombinedFragment) { buildCombinedFragment((CombinedFragment) fragment); return; } if (fragment instanceof InteractionOperand) { unsupportedFragment(fragment); return; } if (fragment instanceof Continuation) { unsupportedFragment(fragment); return; } if (fragment instanceof Interaction) { unsupportedFragment(fragment); return; } if (fragment instanceof InteractionUse) { unsupportedFragment(fragment); return; } if (fragment instanceof ExecutionOccurrenceSpecification) { unsupportedFragment(fragment); return; } if (fragment instanceof MessageOccurrenceSpecification) { MessageOccurrenceSpecification messageEnd = (MessageOccurrenceSpecification) fragment; Lifeline lifeline = ensureSingleCovered(messageEnd); if (lifeline == null) { warning("MessageOccurrenceSpecification without a lifeline: " + messageEnd); return; } Message message = messageEnd.getMessage(); if (message == null) { processPossibleExecutionFinish(orderedFragments, messageEnd, lifeline); return; } if (message.getSendEvent() == messageEnd) { buildMessageSource(orderedFragments, messageEnd); return; } if (message.getReceiveEvent() == fragment) { buildMessageTarget(orderedFragments, messageEnd); return; } } if (fragment instanceof ExecutionSpecification) { buildExecutionSpecification((ExecutionSpecification) fragment); return; } } private void processPossibleExecutionFinish(Iterator<InteractionFragment> orderedFragments, MessageOccurrenceSpecification messageEnd, Lifeline lifeline) { ExecutionSpecification umlFinishedExecution = myStartsAndFinishes.findFinishedExecution(messageEnd); if (umlFinishedExecution == null) { warning("Lost message end (no message) is found (will be ignored):" + messageEnd); return; } if (ensureSingleCovered(umlFinishedExecution) != lifeline) { throw new UMLModelProblem("Execution is finished at wrong lifeline: " + umlFinishedExecution + ", expected lifeline is: " + lifeline); } SDBracketContainer sdFinishedContainer = myCallStack.peek(lifeline); if (false == sdFinishedContainer instanceof SDBehaviorSpec && ((SDBehaviorSpec) sdFinishedContainer).getUmlExecutionSpec() != umlFinishedExecution) { throw new UMLModelProblem("ExecutionSpecification finished: " + umlFinishedExecution + ", while active bracket container was :" + sdFinishedContainer); } if (sdFinishedContainer instanceof SDInvocation && ((SDInvocation) sdFinishedContainer).getUmlExecutionSpec() == null) { throw new SDBuilderInternalProblem("SDInvocation : " + sdFinishedContainer + " does not have uml counterpart. However, we have found finish for it: " + messageEnd + ", actual umlExecution: " + umlFinishedExecution); } myCallStack.pop(lifeline); // if (sdFinishedContainer instanceof SDExecution) { SDExecution sdFinishedExecution = (SDExecution) sdFinishedContainer; SDInvocation sdInvocation = sdFinishedExecution.getInvocation(); if (sdInvocation != null && sdInvocation.getUmlExecutionSpec() == null) { //this invocation was created manually in builder without uml-counterpart, //it means that we won't find finish for it and should remove it manually SDLifeLine invocationLifeLine = sdInvocation.getCoveredLifeLine(); if (invocationLifeLine == null) { throw new SDBuilderInternalProblem("Can't find lifeline for 'auxiliary' SDInvocation: " + sdInvocation); } myCallStack.pop(invocationLifeLine.getUmlLifeline()); } } } private void buildExecutionSpecification(ExecutionSpecification umlExecutionSpec) { Lifeline umlLifeline = ensureSingleCovered(umlExecutionSpec); if (umlLifeline == null) { warning("ExecutionSpecification without lifeline, ignored: " + umlExecutionSpec); return; } SDBracketContainer active = myCallStack.peek(umlLifeline); //it should be bracket for this execution spec; if (false == active instanceof SDBehaviorSpec) { throw new UMLModelProblem("Lost ExecutionSpecification found: " + umlExecutionSpec + ", active bracket container :" + active); } SDBehaviorSpec activeSpec = (SDBehaviorSpec) active; if (activeSpec.getUmlExecutionSpec() != umlExecutionSpec) { //in case of self message we can receive the umlSpec for invocation first, and it is not active //because the active one is the inner execution if (!isSelfMessageExecution(activeSpec)) { throw new UMLModelProblem("Lost ExecutionSpecification found: " + umlExecutionSpec + ", active bracket container :" + active); } SDExecution execution = (SDExecution) active; SDInvocation invocation = execution.getInvocation(); if (umlExecutionSpec != invocation.getUmlExecutionSpec()) { throw new UMLModelProblem("Self message found, but executionSpecification for its invocation is wrong: " + umlExecutionSpec + ", active bracket container :" + active + ", expected invocation execSpec: " + invocation.getUmlExecutionSpec()); } } //everything is fine, we already have behaviorSpec for this umlExecutionSpec -- nothing to do } private boolean isSelfMessageExecution(SDBehaviorSpec spec) { if (false == spec instanceof SDExecution) { return false; } SDExecution execution = (SDExecution) spec; SDInvocation invocation = execution.getInvocation(); if (invocation == null) { return false; } Lifeline executionLL = ensureSingleCovered(execution.getUmlExecutionSpec()); Lifeline invocationLL = ensureSingleCovered(invocation.getUmlExecutionSpec()); return executionLL != null && executionLL == invocationLL; } private void buildMessageTarget(Iterator<InteractionFragment> orderedFragments, MessageOccurrenceSpecification messageTarget) { Message message = messageTarget.getMessage(); MessageEnd sendEvent = message.getSendEvent(); if (sendEvent == null) { buildFoundMessage(messageTarget); return; } if (sendEvent instanceof Gate) { buildGateMessage(messageTarget, (Gate) sendEvent, true); return; } MessageOccurrenceSpecification messageSource = (MessageOccurrenceSpecification) sendEvent; throw new UMLModelProblem("Message " + message + " is sent from the future: " + messageSource); } private void buildMessageSource(Iterator<InteractionFragment> orderedFragments, MessageOccurrenceSpecification messageSource) { Message message = messageSource.getMessage(); MessageEnd receiveEvent = message.getReceiveEvent(); if (receiveEvent == null) { buildLostMessage(messageSource); return; } if (receiveEvent instanceof Gate) { buildGateMessage(messageSource, (Gate) receiveEvent, false); return; } MessageOccurrenceSpecification messageTarget = (MessageOccurrenceSpecification) receiveEvent; boolean targetFound = false; while (orderedFragments.hasNext()) { InteractionFragment nextBetweenSendAndReceive = orderedFragments.next(); if (nextBetweenSendAndReceive == messageTarget) { targetFound = true; break; } warning("Interaction fragment found between message send and receive for message: " + message + ", that is: " + nextBetweenSendAndReceive + ". Will be ignored"); } if (!targetFound) { throw new UMLModelProblem("Message " + message + " is sent to the past"); } if (message.getMessageSort() == MessageSort.REPLY_LITERAL) { buildReplyMessage(orderedFragments, messageSource, messageTarget); return; } else { buildCompleteMessage(orderedFragments, messageSource, messageTarget); return; } } private void buildCompleteMessage(Iterator<InteractionFragment> orderedFragments, MessageOccurrenceSpecification messageSource, MessageOccurrenceSpecification messageTarget) { Message umlMessage = messageSource.getMessage(); Lifeline umlSendingLifeline = ensureSingleCovered(messageSource); Lifeline umlReceivingLifeline = ensureSingleCovered(messageTarget); if (umlSendingLifeline == null) { throw new UMLModelProblem("Message " + umlMessage + " has start :" + messageSource + " which does not belong to lifeline"); } if (umlReceivingLifeline == null) { throw new UMLModelProblem("Message " + umlMessage + " has target :" + messageTarget + " which does not belong to lifeline"); } ExecutionSpecification umlInvocation = myStartsAndFinishes.findStartedExecution(messageSource); //may be null ExecutionSpecification umlExecution = myStartsAndFinishes.findStartedExecution(messageTarget); if (umlExecution == null) { throw new UMLModelProblem("Message " + umlMessage + " does not have receiving ExecutionSpecification at receiveEvent: " + messageTarget); } SDMessage sdMessage = getTraceImpl().bindNewMessage(umlMessage); SDInvocation sdInvocation = getTraceImpl().bindNewInvocation(umlInvocation); SDExecution sdExecution = getTraceImpl().bindNewExecution(umlExecution); sdInvocation.setOutgoingMessage(sdMessage); sdExecution.setIncomingMessage(sdMessage); sdInvocation.setReceiveExecution(sdExecution); //sdExecution.setInvocation(sdInvocation); -- auto (bidi) sdExecution.setUmlStart(messageTarget); sdExecution.setUmlFinish(umlExecution.getFinish()); if (umlInvocation != null) { sdInvocation.setUmlStart(messageSource); sdInvocation.setUmlFinish(umlInvocation.getFinish()); } mySDModel.getMessages().add(sdMessage); SDBracketContainer sdSendingContainer = myCallStack.peek(umlSendingLifeline); sdSendingContainer.getBrackets().add(sdInvocation); myCallStack.push(umlSendingLifeline, sdInvocation); //important to call after push for sending lifeline (consider self calls) SDBracketContainer sdReceivingContainer = myCallStack.peek(umlReceivingLifeline); sdReceivingContainer.getBrackets().add(sdExecution); myCallStack.push(umlReceivingLifeline, sdExecution); } private void buildGateMessage(MessageOccurrenceSpecification umlMessageEnd, Gate umlGate, boolean fromNotToGate) { Lifeline umlLifeline = ensureSingleCovered(umlMessageEnd); if (umlLifeline == null) { warning("MessageOccurrenceSpecification without a lifeline: " + umlMessageEnd); return; } SDBracketContainer sdContainer = myCallStack.peek(umlLifeline); SDGateMessageEnd sdPureOccurrence = getTraceImpl().bindGateMessageEnd(umlMessageEnd); sdPureOccurrence.setIsStartNotFinish(!fromNotToGate); sdContainer.getBrackets().add(sdPureOccurrence); SDGateMessage sdMessageToGate = getTraceImpl().bindGateMessage(umlMessageEnd.getMessage()); sdMessageToGate.setFromNotToGate(fromNotToGate); sdMessageToGate.setGate(SDModelHelper.findGate(mySDModel, umlGate)); sdMessageToGate.setNormalEnd(sdPureOccurrence); mySDModel.getMessages().add(sdMessageToGate); } private void buildLostMessage(MessageOccurrenceSpecification messageSource) { unsupportedFragment(messageSource); } private void buildReplyMessage(Iterator<InteractionFragment> orderedFragments, MessageOccurrenceSpecification replySource, MessageOccurrenceSpecification replyTarget) { throw new SDBuilderInternalProblem("Reply-message is not supported: " + replySource.getMessage()); } private void buildFoundMessage(MessageOccurrenceSpecification messageTarget) { unsupportedFragment(messageTarget); } private void unsupportedFragment(InteractionFragment fragment) { String metaclass = fragment.eClass().getName(); throw new SDBuilderInternalProblem(metaclass + " is not supported: " + fragment); } private void buildSimpleNode(InteractionFragment umlFragment) { Lifeline umlLifeline = ensureSingleCovered(umlFragment); if (umlLifeline == null) { return; } SDBracketContainer sdContainer = myCallStack.peek(umlLifeline); SDSimpleNode sdInvariant = getTraceImpl().bindNewSimpleNode(umlFragment); sdContainer.getBrackets().add(sdInvariant); } private void buildCombinedFragment(CombinedFragment umlFragment) { if (umlFragment.getCovereds().isEmpty()) { throw new UMLModelProblem("Combined fragment without a covered lifelines found: " + umlFragment); } for (Lifeline nextUmlCovered : umlFragment.getCovereds()) { SDBracketContainer bracketContainer = myCallStack.peek(nextUmlCovered); if (bracketContainer instanceof SDBehaviorSpec) { throw new UMLModelProblem(// "Combined fragment " + umlFragment + // " covering lifeline: " + nextUmlCovered + // ", which is in the middle of the message-chain, active bracket: " + bracketContainer); } if (bracketContainer instanceof SDMountingRegion) { SDMountingRegion activeRegion = (SDMountingRegion) bracketContainer; if (activeRegion.getFrame() != myCurrentFrameContainer) { throw new UMLModelProblem(// "Combined fragment " + umlFragment + " covering lifeline: " + nextUmlCovered + // ", can not match mounting regions for parent frame: " + myCurrentFrameContainer + ", actual frame found: " + activeRegion.getFrame()); } } if (bracketContainer instanceof SDLifeLine) { //XXX: we should check consistency of frames.getCovered() before and transform this to new internalSDBuilderProblem (?) //same for above if (myCurrentFrameContainer != mySDModel) { throw new UMLModelProblem(// "Combined fragment " + umlFragment + " covering lifeline: " + nextUmlCovered + // ", but its parent frame: " + myCurrentFrameContainer + " does not."); } if (((SDLifeLine) bracketContainer).getUmlLifeline() != nextUmlCovered) { throw new SDBuilderInternalProblem("Active SDLifeLine from call-stack " + bracketContainer + ", while expecting SD-wrapper for: " + nextUmlCovered); } } } //so far so good SDCombinedFragment sdFragment = getTraceImpl().bindNewCombinedFragment(umlFragment); myCurrentFrameContainer.getFrames().add(sdFragment); pushMountingRegionsForAllCovereds(sdFragment, umlFragment); for (InteractionOperand nextOperand : umlFragment.getOperands()) { buildInteractionOperand(nextOperand, sdFragment); } popMountingRegionsForAllCovereds(sdFragment, umlFragment); } private void buildInteractionOperand(InteractionOperand umlOperand, SDCombinedFragment sdEnclosingFragment) { assert umlOperand.eContainer() == sdEnclosingFragment.getUmlCombinedFragment(); assert myCurrentFrameContainer == sdEnclosingFragment; if (umlOperand.getCovereds().size() != sdEnclosingFragment.getCoveredLifeLines().size()) { throw new UMLModelProblem("Set of covered lifelines differs for InteractionOperand: " + umlOperand + ", and its enclosing fragment: " + sdEnclosingFragment.getUmlCombinedFragment()); } for (SDLifeLine nextCoveredByCombined : sdEnclosingFragment.getCoveredLifeLines()) { if (!umlOperand.getCovereds().contains(nextCoveredByCombined.getUmlLifeline())) { throw new UMLModelProblem(// "CombinedFragment : " + sdEnclosingFragment.getUmlCombinedFragment() + // " covers lifeline: " + nextCoveredByCombined.getUmlLifeline() + // " while its operand : " + umlOperand + " does not."); } } for (Lifeline nextUmlCovered : umlOperand.getCovereds()) { SDBracketContainer bracketContainer = myCallStack.peek(nextUmlCovered); if (false == bracketContainer instanceof SDMountingRegion) { throw new UMLModelProblem(// "Interaction operand : " + umlOperand + // " found, while the message chain on lifeline: " + nextUmlCovered + // " is not completed, active bracket: " + bracketContainer); } } // SDInteractionOperand sdOperand = getTraceImpl().bindNewInteractionOperand(umlOperand); sdEnclosingFragment.getFrames().add(sdOperand); pushMountingRegionsForAllCovereds(sdOperand, umlOperand); for (Iterator<InteractionFragment> innerFragments = umlOperand.getFragments().iterator(); innerFragments.hasNext();) { buildBrackets(innerFragments); } popMountingRegionsForAllCovereds(sdOperand, umlOperand); } private void pushMountingRegionsForAllCovereds(SDFrame sdFrame, InteractionFragment umlFrame) { for (Lifeline nextUmlCovered : umlFrame.getCovereds()) { SDBracketContainer bracketContainer = myCallStack.peek(nextUmlCovered); assert bracketContainer.getCoveredLifeLine().getUmlLifeline() == nextUmlCovered; sdFrame.getCoveredLifeLines().add(bracketContainer.getCoveredLifeLine()); SDMountingRegion nextRegion = getTraceImpl().bindNewMountingRegion(sdFrame); bracketContainer.getBrackets().add(nextRegion); myCallStack.push(nextUmlCovered, nextRegion); } myCurrentFrameContainer = sdFrame; } private void popMountingRegionsForAllCovereds(SDFrame sdFrame, InteractionFragment umlFrame) { for (Lifeline nextUmlCovered : umlFrame.getCovereds()) { SDBracketContainer bracketContainer = myCallStack.peek(nextUmlCovered); if (false == bracketContainer instanceof SDMountingRegion) { throw new SDBuilderInternalProblem(// "After processing contents for " + umlFrame + // ", can't match the active bracket container for lifeline: " + nextUmlCovered + // ", actual : " + bracketContainer + ", while expecting mounting region "); } SDMountingRegion nextRegion = (SDMountingRegion) bracketContainer; if (nextRegion.getFrame() != sdFrame) { throw new SDBuilderInternalProblem(// "After processing contents for " + umlFrame + // ", can't match frame for mounting region: " + nextRegion + // ", actual frame: " + nextRegion.getFrame() + ", while expecting: " + sdFrame); } myCallStack.pop(nextUmlCovered); } if (myCurrentFrameContainer != sdFrame) { throw new SDBuilderInternalProblem("Current frame container: " + myCurrentFrameContainer + ", while expecting: " + sdFrame); } myCurrentFrameContainer = sdFrame.getFrameContainer(); } private static Lifeline ensureSingleCovered(InteractionFragment fragment) { List<Lifeline> covered = fragment.getCovereds(); if (covered.size() > 1) { throw new UMLModelProblem("Expected single covered lifeline for: " + fragment + ", actually: " + fragment.getCovereds()); } return covered.isEmpty() ? null : covered.get(0); } private static void warning(String message) { // } private SDBuilderTrace getTraceImpl() { return (SDBuilderTrace) getSDModel().getUMLTracing(); } }