package org.eclipse.papyrus.uml.diagram.sequence.validation; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import org.eclipse.core.runtime.IStatus; import org.eclipse.draw2d.geometry.Point; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.validation.IValidationContext; import org.eclipse.gef.EditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.osgi.util.NLS; import org.eclipse.papyrus.uml.diagram.common.util.DiagramEditPartsUtil; import org.eclipse.papyrus.uml.diagram.common.util.MDTUtil; import org.eclipse.papyrus.uml.diagram.sequence.edit.parts.LifelineEditPart; import org.eclipse.papyrus.uml.diagram.sequence.part.UMLVisualIDRegistry; import org.eclipse.papyrus.uml.diagram.sequence.util.SequenceUtil; import org.eclipse.ui.IEditorPart; import org.eclipse.uml2.uml.DestructionOccurrenceSpecification; import org.eclipse.uml2.uml.ExecutionOccurrenceSpecification; import org.eclipse.uml2.uml.ExecutionSpecification; import org.eclipse.uml2.uml.GeneralOrdering; import org.eclipse.uml2.uml.Interaction; import org.eclipse.uml2.uml.InteractionFragment; import org.eclipse.uml2.uml.InteractionOperand; 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.OccurrenceSpecification; /** * This class is a validator which ensures that :<br/> * <li> * <ul> * In the order Interaction's fragment property, the order is kept as a valid trace, * </ul> * <ul> * Or at least, when there is no valid trace, that the order is kept for each lifeline (not to loose sequence information on a lifeline, which is * contained only graphically otherwise). * </ul> * </li> <br/> * Note that this class contains an algorithm which computes a valid trace. With little adaptation, it could easily provide all valid traces, hence, * compare two lifelines to know whether they are equivalent. * * @author vhemery */ public class FragmentOrderingKeeper { /** A constant equals to half the smallest location delta */ private static final float HALF_UNIT = (float)0.5; /** Format for displaying an element's name */ private static final String NAME_FORMAT = "<{0}> {1}"; /** map with, for a given index, the fragments which have been put with an optional order (may change) */ private Map<Integer, List<InteractionFragment>> optionallyOrderedFragments; private EList<InteractionFragment> orderedFragments; private Set<Lifeline> constrainingNotRepresentedLifelines; private Set<LifelineEditPart> constrainingLifelineParts; private Set<Message> constrainingMessages; private Set<ExecutionSpecification> constrainingExecutions; private Set<GeneralOrdering> constrainingGeneralOrderings; /* private Set<CombinedFragment> constrainingCombinedFragments; */ private List<List<InteractionFragment>> orderConstraints; private Set<InteractionFragment> conflictingFragments; /** * Validate modification and update the interaction's fragments order if necessary. * * @see org.eclipse.emf.validation.AbstractModelConstraint#validate(org.eclipse.emf.validation.IValidationContext) * * @param target * the target to validate * @param ctx * validation context * @return status */ public IStatus validate(EObject target, IValidationContext ctx) { if(target instanceof Interaction) { Interaction interaction = (Interaction)target; boolean valid = validateOrder(interaction); if(!valid) { IStatus status = ctx.createFailureStatus(getConflictingFragmentsFormattedString()); removeModelReferences(); return status; } } else if(target instanceof InteractionOperand) { InteractionOperand interactionOp = (InteractionOperand)target; boolean valid = validateOrder(interactionOp); if(!valid) { IStatus status = ctx.createFailureStatus(getConflictingFragmentsFormattedString()); removeModelReferences(); return status; } } else if(target instanceof InteractionFragment) { InteractionFragment fragment = (InteractionFragment)target; boolean valid = validateOrder(fragment.getEnclosingInteraction()); if(!valid) { IStatus status = ctx.createFailureStatus(getConflictingFragmentsFormattedString()); removeModelReferences(); return status; } } removeModelReferences(); return ctx.createSuccessStatus(); } /** * Get a formatted string with names of all conflicting fragments * * @return formatted string */ private String getConflictingFragmentsFormattedString() { StringBuffer buff = new StringBuffer(); for(InteractionFragment frag : conflictingFragments) { buff.append(System.getProperty("line.separator")); String name = NLS.bind(NAME_FORMAT, frag.eClass().getName(), frag.getQualifiedName()); buff.append(name); } return buff.toString(); } /** * Remove any reference to model elements to avoid memory loss */ private void removeModelReferences() { // orderedFragments is an EList, hence, we must not empty it. orderedFragments = null; // other lists can be cleared. if(constrainingLifelineParts != null) { constrainingLifelineParts.clear(); } if(constrainingMessages != null) { constrainingMessages.clear(); } if(constrainingExecutions != null) { constrainingExecutions.clear(); } if(constrainingGeneralOrderings != null) { constrainingGeneralOrderings.clear(); } //constrainingCombinedFragments.clear(); if(orderConstraints != null) { orderConstraints.clear(); } if(conflictingFragments != null) { conflictingFragments.clear(); } if(optionallyOrderedFragments != null) { optionallyOrderedFragments.clear(); } } /** * Validate the order of interaction operand's fragments * * @param interactionOperand * the interaction operand * @return true if a valid trace has been computed, false if none exists */ private boolean validateOrder(InteractionOperand interactionOperand) { orderedFragments = interactionOperand.getFragments(); computeConstraints(); return reorderFragmentsInAValidTrace(); } /** * Validate the order of interaction's fragments * * @param interaction * the interaction * @return true if a valid trace has been computed, false if none exists */ private boolean validateOrder(Interaction interaction) { orderedFragments = interaction.getFragments(); computeConstraints(); return reorderFragmentsInAValidTrace(); } /** * Compute constraints defined on fragments.<br/> * These are given by (by priority order):<br/> * <li> * <ul> * Lifelines (graphical location and previous order of elements not drawn) * </ul> * <ul> * Combined Fragments (graphical order of InteractionOperands in case no Lifeline is covered) * </ul> * <ul> * Messages (send > receive) * </ul> * ExecutionSpecifications (start > execution > finish) * <ul> * <ul> * General Orderings (before > after) * </ul> * </li> */ private void computeConstraints() { IEditorPart editor = MDTUtil.getActiveEditor(); DiagramEditPart diagram = null; if(editor instanceof IDiagramWorkbenchPart) { diagram = ((IDiagramWorkbenchPart)editor).getDiagramEditPart(); } // reset constraints constrainingNotRepresentedLifelines = new HashSet<Lifeline>(); constrainingLifelineParts = new HashSet<LifelineEditPart>(); /* constrainingCombinedFragments = new HashSet<CombinedFragment>(); */ constrainingMessages = new HashSet<Message>(); constrainingExecutions = new HashSet<ExecutionSpecification>(); constrainingGeneralOrderings = new HashSet<GeneralOrdering>(); // get new constraining objects for(InteractionFragment fragment : orderedFragments) { // get Lifelines for(Lifeline lifeline : fragment.getCovereds()) { boolean lifelineRepresented = false; List<?> lifelineViews = DiagramEditPartsUtil.getEObjectViews(lifeline); for(Object lifelineView : lifelineViews) { if(lifelineView instanceof View && diagram != null) { int visualID = UMLVisualIDRegistry.getVisualID((View)lifelineView); if(visualID == LifelineEditPart.VISUAL_ID) { EditPart part = DiagramEditPartsUtil.getEditPartFromView((View)lifelineView, diagram); if(part instanceof LifelineEditPart) { constrainingLifelineParts.add((LifelineEditPart)part); lifelineRepresented = true; } } } } if(!lifelineRepresented) { // rely on old order for the lifeline constrainingNotRepresentedLifelines.add(lifeline); } } // get usefull Combined Fragments. Removed, since Combined Fragments own themselves their operands /* * if(fragment.getCovereds().isEmpty()) { * if(fragment instanceof CombinedFragment) { * constrainingCombinedFragments.add((CombinedFragment)fragment); * } else if(fragment instanceof InteractionOperand) { * Element owningCF = fragment.getOwner(); * if(owningCF instanceof CombinedFragment) { * constrainingCombinedFragments.add((CombinedFragment)owningCF); * } * } * } */ // get Messages if(fragment instanceof MessageOccurrenceSpecification) { Message mess = ((MessageOccurrenceSpecification)fragment).getMessage(); if(mess != null) { constrainingMessages.add(mess); } } // get ExecutionSpecifications if(fragment instanceof ExecutionSpecification) { constrainingExecutions.add((ExecutionSpecification)fragment); } else if(fragment instanceof ExecutionOccurrenceSpecification) { ExecutionSpecification exe = ((ExecutionOccurrenceSpecification)fragment).getExecution(); if(exe != null) { constrainingExecutions.add(exe); } } // get General Orderings if(fragment instanceof OccurrenceSpecification) { EList<GeneralOrdering> orderings = ((OccurrenceSpecification)fragment).getToAfters(); constrainingGeneralOrderings.addAll(orderings); orderings = ((OccurrenceSpecification)fragment).getToBefores(); constrainingGeneralOrderings.addAll(orderings); } } // construct constrained partial orders constructPartialOrders(); } /** * Compute partial orders given by constraining elements (ordered by priority).<br/> * <li> * <ul> * Lifelines (graphical location and previous order of elements not drawn) * </ul> * <ul> * Combined Fragments (graphical order of InteractionOperands in case no Lifeline is covered) * </ul> * <ul> * Messages (send > receive) * </ul> * ExecutionSpecifications (start > execution > finish) * <ul> * <ul> * General Orderings (before > after) * </ul> * </li> */ private void constructPartialOrders() { optionallyOrderedFragments = new HashMap<Integer, List<InteractionFragment>>(); int numberOfConstraints = constrainingNotRepresentedLifelines.size() + constrainingLifelineParts.size() + constrainingMessages.size() + constrainingExecutions.size() + constrainingGeneralOrderings.size(); /* * + constrainingCombinedFragments.size(); */ orderConstraints = new ArrayList<List<InteractionFragment>>(numberOfConstraints); int indexConstraint = 0; // construct lifelines constraints (model only) for(Lifeline lifeline : constrainingNotRepresentedLifelines) { List<InteractionFragment> constraint = new ArrayList<InteractionFragment>(); //fill constraint : previous order for elements of lifeline not drawn for(InteractionFragment fragment : orderedFragments) { if(lifeline.getCoveredBys().contains(fragment)) { constraint.add(fragment); } } // store constraint orderConstraints.add(indexConstraint, constraint); indexConstraint++; } // construct lifelines constraints (graphical and model) for(LifelineEditPart part : constrainingLifelineParts) { TreeMap<Float, InteractionFragment> constraint = new TreeMap<Float, InteractionFragment>(); //fill constraint : graphical location and previous order of elements not drawn fillConstraintWithLifelineEvents(constraint, part, indexConstraint); // store constraint orderConstraints.add(indexConstraint, new ArrayList<InteractionFragment>(constraint.values())); indexConstraint++; } // construct general orderings constraints. Removed, since Combined Fragments own themselves their operands /* * for(CombinedFragment combFrag : constrainingCombinedFragments) { * List<InteractionFragment> constraint = new ArrayList<InteractionFragment>(); * //fill constraint : graphical order of InteractionOperands * // take model order of InteractionOperands, we ensure it is coherent with graphical order * constraint.addAll(combFrag.getOperands()); * // store constraint * orderConstraints.add(indexConstraint, constraint); * indexConstraint++; * } */ // construct messages constraints for(Message mess : constrainingMessages) { List<InteractionFragment> constraint = new ArrayList<InteractionFragment>(2); //fill constraint : send > receive MessageEnd frag = mess.getSendEvent(); if(frag instanceof InteractionFragment && orderedFragments.contains(frag)) { constraint.add((InteractionFragment)frag); } frag = mess.getReceiveEvent(); if(frag instanceof InteractionFragment && orderedFragments.contains(frag)) { constraint.add((InteractionFragment)frag); } // store constraint orderConstraints.add(indexConstraint, constraint); indexConstraint++; } // construct executions constraints for(ExecutionSpecification exe : constrainingExecutions) { List<InteractionFragment> constraint = new ArrayList<InteractionFragment>(3); //fill constraint : start > execution > finish InteractionFragment frag = exe.getStart(); if(frag != null && orderedFragments.contains(frag)) { constraint.add(frag); } frag = exe; if(orderedFragments.contains(frag)) { constraint.add(frag); } frag = exe.getFinish(); if(frag != null && orderedFragments.contains(frag)) { constraint.add(frag); } // store constraint orderConstraints.add(indexConstraint, constraint); indexConstraint++; } // construct general orderings constraints for(GeneralOrdering genOrd : constrainingGeneralOrderings) { List<InteractionFragment> constraint = new ArrayList<InteractionFragment>(2); //fill constraint : before > after InteractionFragment frag = genOrd.getBefore(); if(frag != null && orderedFragments.contains(frag)) { constraint.add(frag); } frag = genOrd.getAfter(); if(frag != null && orderedFragments.contains(frag)) { constraint.add(frag); } // store constraint orderConstraints.add(indexConstraint, constraint); indexConstraint++; } } /** * Fill the constraint imposed by a lifeline * * @param constraint * the tree map for hosting the constraint * @param lifelinePart * the lifeline edit part * @param indexConstraint * index of constraint */ private void fillConstraintWithLifelineEvents(TreeMap<Float, InteractionFragment> constraint, LifelineEditPart lifelinePart, int indexConstraint) { EObject lifeline = lifelinePart.resolveSemanticElement(); if(lifeline instanceof Lifeline) { List<InteractionFragment> nonLocalizedEvents = new ArrayList<InteractionFragment>(); // sort fragments according to their location on the lifeline for(InteractionFragment event : ((Lifeline)lifeline).getCoveredBys()) { if(orderedFragments.contains(event)) { Point loc = SequenceUtil.findLocationOfEvent(lifelinePart, event); if(loc != null) { float index = findNonConflictingYIndexOnLifeline(loc.y, constraint, event); constraint.put(index, event); } else { nonLocalizedEvents.add(event); } } // else, it is not in the list, must be in a child element. } optionallyOrderedFragments.put(indexConstraint, nonLocalizedEvents); // add not drawn events according to their old order in the valid trace InteractionFragment lastMetSortedFragment = null; for(InteractionFragment fragment : orderedFragments) { if(((Lifeline)lifeline).getCoveredBys().contains(fragment)) { // this is a fragment of the lifeline. if(constraint.containsValue(fragment)) { lastMetSortedFragment = fragment; } else if(nonLocalizedEvents.contains(fragment) && lastMetSortedFragment == null) { // insert it at the very beginning constraint.put((float)0, fragment); lastMetSortedFragment = fragment; } else if(nonLocalizedEvents.contains(fragment)) { // insert it just after lastMetSortedFragment Iterator<Entry<Float, InteractionFragment>> entryIt = constraint.entrySet().iterator(); // find float key of lastMetSortedFragment while(entryIt.hasNext()) { Entry<Float, InteractionFragment> entry = entryIt.next(); if(entry.getValue().equals(lastMetSortedFragment)) { if(entryIt.hasNext()) { // insert between the two fragments float key = (entry.getKey() + entryIt.next().getKey()) / 2; constraint.put(key, fragment); lastMetSortedFragment = fragment; break; } else { // insert at the end float key = entry.getKey() + 1; constraint.put(key, fragment); lastMetSortedFragment = fragment; break; } } } } } } } } /** * Find a float index corresponding approximatively to the location of event on the lifeline. * * @param y * real location of event on the lifeline * @param eventsMap * map containing other events of the lifeline * @param event * event to find a float index for * @return unused float index for the map */ private float findNonConflictingYIndexOnLifeline(int y, TreeMap<Float, InteractionFragment> eventsMap, InteractionFragment event) { float index = Integer.valueOf(y).floatValue(); float delta = HALF_UNIT; while(eventsMap.containsKey(index)) { InteractionFragment otherEvent = eventsMap.get(index); if(simulatenousEventsHappenInThisOrder(otherEvent, event)) { // other event must happen before the event index += delta; } else { // event must happen before the other event index -= delta; } delta = delta * HALF_UNIT; } return index; } /** * Gives the order between two fragments which virtually happen at the same time * * @param firstFragment * first InteractionFragment * @param secondFragment * second InteractionFragment * @return true if firstEvent should happen before secondEvent, false if the contrary. */ private boolean simulatenousEventsHappenInThisOrder(InteractionFragment firstFragment, InteractionFragment secondFragment) { // DestructionEvent comes last if(firstFragment instanceof DestructionOccurrenceSpecification) { return false; } else if(secondFragment instanceof DestructionOccurrenceSpecification) { return true; } // a receiving message event should trigger any other event if(firstFragment instanceof MessageOccurrenceSpecification && EcoreUtil.equals(((MessageOccurrenceSpecification)firstFragment).getMessage().getReceiveEvent(), firstFragment)) { return true; } if(secondFragment instanceof MessageOccurrenceSpecification && EcoreUtil.equals(((MessageOccurrenceSpecification)secondFragment).getMessage().getReceiveEvent(), secondFragment)) { return false; } // a starting execution event should happen before subsequent execution's events (except creation message receive) if(firstFragment instanceof ExecutionOccurrenceSpecification && EcoreUtil.equals(((ExecutionOccurrenceSpecification)firstFragment).getExecution().getStart(), firstFragment)) { return true; } if(secondFragment instanceof ExecutionOccurrenceSpecification && EcoreUtil.equals(((ExecutionOccurrenceSpecification)secondFragment).getExecution().getStart(), secondFragment)) { return false; } // an execution event should happen before subsequent execution's events (except starting) if(firstFragment instanceof ExecutionSpecification) { return true; } if(secondFragment instanceof ExecutionSpecification) { return false; } // a finishing execution event should happen after terminated execution's events if(firstFragment instanceof ExecutionOccurrenceSpecification && EcoreUtil.equals(((ExecutionOccurrenceSpecification)firstFragment).getExecution().getFinish(), firstFragment)) { return false; } if(secondFragment instanceof ExecutionOccurrenceSpecification && EcoreUtil.equals(((ExecutionOccurrenceSpecification)secondFragment).getExecution().getFinish(), secondFragment)) { return true; } // a sending message event should happen preferably after an unspecified event if(firstFragment instanceof MessageOccurrenceSpecification && EcoreUtil.equals(((MessageOccurrenceSpecification)firstFragment).getMessage().getSendEvent(), firstFragment)) { return false; } if(secondFragment instanceof MessageOccurrenceSpecification && EcoreUtil.equals(((MessageOccurrenceSpecification)secondFragment).getMessage().getSendEvent(), secondFragment)) { return true; } // otherwise, no importance return true; } /** * Reorder the fragments according to constraints to make a valid trace. * * NOTE : this algorithm could easily be adapted to compute all valid traces. * * @return true if a valid trace has been computed, false if there is no valid trace */ private boolean reorderFragmentsInAValidTrace() { List<InteractionFragment> initialFragmentsList = new ArrayList<InteractionFragment>(orderedFragments); List<InteractionFragment> reorderedFragments = new ArrayList<InteractionFragment>(orderedFragments.size()); /* * This algorithm merges constraints to compute a valid trace. * With little adaptation, it could compute all valid traces. */ // n : number of constraint lists int n = orderConstraints.size(); // whether computed trace is valid (also whether a valid trace exists) boolean valid = true; // pointers to first elements to handle in each constraint list int[] pointers = new int[n]; Arrays.fill(pointers, 0); while(getFragmentToInspect(-1, pointers) != null) { // inspect each constraint list to know whether fragment is mature enough, in such a case, store it with constraint index Map<Integer, InteractionFragment> matureFragments = new HashMap<Integer, InteractionFragment>(n); for(int i = 0; i < n; i++) { InteractionFragment fragmentToInspect = getFragmentToInspect(i, pointers); // if no more fragment in this constraint (fragmentToInspect == null), nothing to do if(fragmentToInspect != null) { /* * Check whether fragment is mature enough : * Fragment can happen only if other constraints do not impose it to happen after their current fragment. */ boolean wait = false; for(int j = 0; j < n; j++) { if(i != j) { // check that current fragment of constraint j must not occur before if(pointers[j] < orderConstraints.get(j).size()) { List<InteractionFragment> waitingList = orderConstraints.get(j).subList(pointers[j] + 1, orderConstraints.get(j).size()); if(waitingList.contains(fragmentToInspect)) { wait = true; break; } } } } if(!wait) { //Fragment is ready to happen. matureFragments.put(i, fragmentToInspect); } } } if(matureFragments.isEmpty()) { //FIXME duplicated code : try again, without optional order // inspect each constraint list to know whether fragment is mature enough, in such a case, store it with constraint index matureFragments = new HashMap<Integer, InteractionFragment>(n); for(int i = 0; i < n; i++) { InteractionFragment fragmentToInspect = getFragmentToInspect(i, pointers); // if no more fragment in this constraint (fragmentToInspect == null), nothing to do if(fragmentToInspect != null) { /* * Check whether fragment is mature enough : * Fragment can happen only if other constraints do not impose it to happen after their current fragment. */ boolean wait = false; for(int j = 0; j < n; j++) { if(i != j) { // check that current fragment of constraint j must not occur before if(pointers[j] < orderConstraints.get(j).size()) { // only difference : ignore order for fragments in optionallyOrderedFragments int updatedPointer = pointers[j]; if(optionallyOrderedFragments.containsKey(j)) { List<InteractionFragment> ignore = optionallyOrderedFragments.get(j); /* * // hack for coregion * while(ignore.contains(orderConstraints.get(j).get(updatedPointer)) || * (orderConstraints.get(j).get(updatedPointer) instanceof CombinedFragment && * InteractionOperatorKind.PAR_LITERAL * .equals(((CombinedFragment)orderConstraints.get(j).get(updatedPointer)).getInteractionOperator()))) { * updatedPointer++; * } */ while(ignore.contains(orderConstraints.get(j).get(updatedPointer))) { updatedPointer++; } } List<InteractionFragment> waitingList = orderConstraints.get(j).subList(updatedPointer + 1, orderConstraints.get(j).size()); if(waitingList.contains(fragmentToInspect)) { wait = true; break; } } } } if(!wait) { //Fragment is ready to happen. matureFragments.put(i, fragmentToInspect); } } } if(matureFragments.isEmpty()) { // no valid trace. if(valid) { // store first conflicting fragments for user explanation conflictingFragments = new HashSet<InteractionFragment>(n); for(int k = 0; k < n; k++) { InteractionFragment frag = getFragmentToInspect(k, pointers); if(frag != null) { conflictingFragments.add(frag); } } } valid = false; /* * We must at least keep order of constraints with higher priority. * Take the next event in the first available constraint. */ InteractionFragment addedFragment = getFragmentToInspect(-1, pointers); reorderedFragments.add(addedFragment); // increment pointers of constraints whose fragment has been added. for(int k = 0; k < n; k++) { InteractionFragment frag = getFragmentToInspect(k, pointers); while(reorderedFragments.contains(frag)) { // either frag == addedFragment and has just been added, or it has been added earlier after a conflict pointers[k]++; frag = getFragmentToInspect(k, pointers); } } } else { /* * NOTE : to compute every traces, fork here by making fragmentsSet.size() branches, adding only one mature fragment for each * branch and continuing the algorithm. */ // All matureFragments can happen in any order. Add them all (without doubles). Set<InteractionFragment> fragmentsSet = new HashSet<InteractionFragment>(matureFragments.values()); reorderedFragments.addAll(fragmentsSet); // increment pointers of constraints whose fragment has been added. for(int incrementingPointerIndex : matureFragments.keySet()) { InteractionFragment frag = getFragmentToInspect(incrementingPointerIndex, pointers); while(reorderedFragments.contains(frag)) { // either frag == addedFragment and has just been added, or it has been added earlier after a conflict pointers[incrementingPointerIndex]++; frag = getFragmentToInspect(incrementingPointerIndex, pointers); } } } // // no valid trace. // if(valid) { // // store first conflicting fragments for user explanation // conflictingFragments = new HashSet<InteractionFragment>(n); // for(int k = 0; k < n; k++) { // InteractionFragment frag = getFragmentToInspect(k, pointers); // if(frag != null) { // conflictingFragments.add(frag); // } // } // } // valid = false; // /* // * We must at least keep order of constraints with higher priority. // * Take the next event in the first available constraint. // */ // InteractionFragment addedFragment = getFragmentToInspect(-1, pointers); // reorderedFragments.add(addedFragment); // // increment pointers of constraints whose fragment has been added. // for(int k = 0; k < n; k++) { // InteractionFragment frag = getFragmentToInspect(k, pointers); // while(reorderedFragments.contains(frag)) { // // either frag == addedFragment and has just been added, or it has been added earlier after a conflict // pointers[k]++; // frag = getFragmentToInspect(k, pointers); // } // } } else { /* * NOTE : to compute every traces, fork here by making fragmentsSet.size() branches, adding only one mature fragment for each * branch and continuing the algorithm. */ // All matureFragments can happen in any order. Add them all (without doubles). Set<InteractionFragment> fragmentsSet = new HashSet<InteractionFragment>(matureFragments.values()); reorderedFragments.addAll(fragmentsSet); // increment pointers of constraints whose fragment has been added. for(int incrementingPointerIndex : matureFragments.keySet()) { InteractionFragment frag = getFragmentToInspect(incrementingPointerIndex, pointers); while(reorderedFragments.contains(frag)) { // either frag == addedFragment and has just been added, or it has been added earlier after a conflict pointers[incrementingPointerIndex]++; frag = getFragmentToInspect(incrementingPointerIndex, pointers); } } } } // Add fragments which were not in any constraint /* * NOTE : to compute every traces, these can be added wherever in the list. */ initialFragmentsList.removeAll(reorderedFragments); reorderedFragments.addAll(initialFragmentsList); /* * Now that we have a valid trace, apply it on orderedFragments. * Only move operations must be performed on the EList, since others strongly affect the model. */ for(int i = 0; i < reorderedFragments.size(); i++) { orderedFragments.move(i, reorderedFragments.get(i)); } return valid; } /** * Get the fragment to inspect for the trace computing algorithm. * * @param i * index of constraint to take in account, or -1 for taking the first available one with the highest priority * @param pointers * pointers indicating fragments to inspect in each constraint * @return fragment which must be inspected or null if none left */ private InteractionFragment getFragmentToInspect(int i, int[] pointers) { if(orderConstraints.size() != pointers.length) { // should not happen, incorrect pointers argument return null; } if(i < 0 || i >= orderConstraints.size()) { i = -1; // compute best i with highest priority int j = 0; while(j < orderConstraints.size() && i == -1) { if(pointers[j] < orderConstraints.get(j).size()) { // elements left in constraint j. Best constraint index found. i = j; } j++; } } if(0 <= i && i < orderConstraints.size() && pointers[i] < orderConstraints.get(i).size()) { // return next fragment in constraint return orderConstraints.get(i).get(pointers[i]); } else { // no fragment left to inspect return null; } } }