// Copyright 2012 Georg-August-Universität Göttingen, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package de.ugoe.cs.autoquest.tasktrees.temporalrelation; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import de.ugoe.cs.autoquest.tasktrees.taskequality.TaskEquality; import de.ugoe.cs.autoquest.tasktrees.temporalrelation.utils.MostSimilarTaskDeterminer; import de.ugoe.cs.autoquest.tasktrees.temporalrelation.utils.SimilarTasks; import de.ugoe.cs.autoquest.tasktrees.temporalrelation.utils.TaskTraversal; import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration; import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship; import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptional; import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptionalInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskBuilder; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskFactory; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstanceList; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel; import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession; import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskPath; import de.ugoe.cs.util.console.Console; import difflib.Chunk; import difflib.Delta; /** *

* This rule performs a condensing of a task tree. It searches for similar tasks and merges them * if possible in to one task with different execution variants. Hence, this rule detects selections * and optionals. *

*

* The similarity of tasks is determined by comparing task traversals. A task traversal is an * ordered list of the leaf nodes of a task. This is similar to performing a minimal execution of * the task. The task traversals of two tasks are compared using string comparison algorithms. The * less differences the lists have, the more similar they are. *

*

* If two tasks are similar, they are merged. Merging is done based on the differences between * the task traversals. Two tasks are merged based on their instances. First, all instances of both * tasks are flattened. Instances of selections or commonalities of both tasks stay unflattened. * Then the lists resulting from this flattening are extended with instances of optionals and * selections which are introduced where required. Finally, the instances are put together to a * single task again by applying the {@link SequenceForTaskDetectionRule} on them. *

*

* Merging has to consider several specific situations. E.g., tasks may look similar but due to * iterations, they can not be merged correctly. Here, the rule ensures, that these so called * interleaving iterations are detected, not traversed when traversing a task and its instances, * and, therefore, preserved. *

*

* All details about this rule are described in the extended ACHI2014 paper of Harms "Trace-based * task tree generation". The extended paper was submitted to the IntSys IARIA Journal. *

* * @author Patrick Harms */ class CondenseSimilarTasksRule implements ISessionScopeRule { /** *

* the task factory to be used for creating substructures for the temporal * relationships identified during rule application *

*/ private ITaskFactory taskFactory; /** *

* the task builder to be used for creating substructures for the temporal relationships * identified during rule application *

*/ private ITaskBuilder taskBuilder; /** *

* the task handling strategy to be used for comparing tasks during iteration detection an trie * generation, i.e., after the tasks are harmonized *

*/ private TaskHandlingStrategy identTaskHandlStrat; /** *

* instantiates the rule and initializes it with a task equality to be considered when * comparing tasks as well as a task factory and builder to be used for creating task * structures. *

* * @param minimalTaskEquality the task equality to be considered when comparing tasks * @param taskFactory the task factory to be used for creating substructures * @param taskBuilder the task builder to be used for creating substructures */ CondenseSimilarTasksRule(TaskEquality minimalTaskEquality, ITaskFactory taskFactory, ITaskBuilder taskBuilder) { this.taskFactory = taskFactory; this.taskBuilder = taskBuilder; this.identTaskHandlStrat = new TaskHandlingStrategy(TaskEquality.IDENTICAL); } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return "CondenseSimilarTasksRule"; } /* (non-Javadoc) * @see de.ugoe.cs.autoquest.tasktrees.temporalrelation.ISessionScopeRule#apply(java.util.List) */ @Override public RuleApplicationResult apply(List sessions) { // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> /*final List formerInstances = new ArrayList<>(); final List newInstances = new ArrayList<>(); try { ITaskInstanceVisitor visitor = new DefaultTaskInstanceTraversingVisitor() { @Override public void visit(IEventTaskInstance eventTaskInstance) { formerInstances.add(eventTaskInstance); } }; PrintStream out = new PrintStream(new FileOutputStream(new File("01.out"))); for (IUserSession session : sessions) { new TaskTreeEncoder().encode(session, out, null); for (ITaskInstance instance : session) { instance.accept(visitor); } } out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } PrintStream fileout = null; try { fileout = new PrintStream("sysout.txt"); } catch (FileNotFoundException e1) { e1.printStackTrace(); } PrintStream origOut = System.out; if (fileout != null) { System.setOut(fileout); }*/ // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< RuleApplicationData appData = new RuleApplicationData(sessions); do { appData.setTaskModel(taskFactory.createTaskModel(sessions)); appData.setMostSimilarTasks(null); // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>> // for (ITask task : appData.getTaskModel().getTasks()) { // if (task.getInstances().size() <= 0) { // throw new RuntimeException("task " + task + " has no instances anymore"); // } // // try { // new TaskTreeValidator().validate(task); // } // catch (Exception e) { // new TaskTreeEncoder().encode(task, System.err); // } // } // // new TaskTreeValidator().validate(sessions); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<< Console.println("condensing " + appData.getTaskModel().getTasks().size() + " tasks"); getMostSimilarTasks(appData); if (appData.getMostSimilarTasks() != null) { for (SimilarTasks mostSimilarTask : appData.getMostSimilarTasks()) { handleSimilarTasks(mostSimilarTask, appData); appData.markAsAlreadyCondensed (mostSimilarTask.getLeftHandSide(), mostSimilarTask.getRightHandSide()); } } } while (appData.getMostSimilarTasks() != null); // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> /*try { ITaskInstanceVisitor visitor = new DefaultTaskInstanceTraversingVisitor() { @Override public void visit(IEventTaskInstance eventTaskInstance) { newInstances.add(eventTaskInstance); } }; PrintStream out = new PrintStream(new FileOutputStream(new File("02.out"))); for (IUserSession session : sessions) { new TaskTreeEncoder().encode(session, out, null); for (ITaskInstance instance : session) { instance.accept(visitor); } } out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } System.out.println("sizes " + formerInstances.size() + " " + newInstances.size()); for (int i = 0; i < newInstances.size(); i++) { if (formerInstances.get(i) != newInstances.get(i)) { System.out.println(i + " " + formerInstances.get(i) + " " + newInstances.get(i)); } } System.setOut(origOut); fileout.close();*/ // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< return appData.finalizeRuleApplicationResult(); } /** * */ private void getMostSimilarTasks(RuleApplicationData appData) { // determine the list of interesting tasks Collection allTasks = appData.getTaskModel().getTasks(); Iterator taskIterator = allTasks.iterator(); List taskList = new ArrayList(allTasks.size()); while (taskIterator.hasNext()) { ITask task = taskIterator.next(); // only Sequences need to be compared with each other. Iterations differ only in their // child, i.e., in the child sequences. if (task instanceof ISequence) { taskList.add(task); } } if (taskList.size() > 1) { List mostSimilarTasks = appData.getMostSimilarTasksDeterminer().getMostSimilarTasks (taskList, appData.getTaskModel()); if (mostSimilarTasks.size() > 0) { appData.setMostSimilarTasks(mostSimilarTasks); } } } /** * */ private void handleSimilarTasks(SimilarTasks similarTasks, RuleApplicationData appData) { // we need at least one instance per similar task. If not, it will not work and also // does not make sense. No instances anymore can be caused by merging and hence // discarding tasks in preceding merges. // similarTasks.dump(System.out); if ((similarTasks.getLeftHandSide().getInstances().size() <= 0) || (similarTasks.getRightHandSide().getInstances().size() <= 0)) { /*System.out.println("a task exists that has no instances"); System.out.println(similarTasks.getLeftHandSide() + " " + similarTasks.getLeftHandSide().getInstances().size()); System.out.println(similarTasks.getRightHandSide() + " " + similarTasks.getRightHandSide().getInstances().size());*/ throw new RuntimeException ("one and the same task seems to belong to several similar tasks"); } similarTasks = SimilarTasks.getMergableLevelOfSimilarity (similarTasks, identTaskHandlStrat.getTaskComparator()); if (similarTasks == null) { // this may happen, if no mergable level of similarity can be found return; } // similarTasks.dump(System.out); List flattenInstructions = getFlattenInstructions(similarTasks, appData); // for (FlattenInstruction instruction : flattenInstructions) { // instruction.dump(System.out); // } int noOfFlattenedInstances = similarTasks.getLeftHandSide().getInstances().size() + similarTasks.getRightHandSide().getInstances().size(); // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>> List excludes = new ArrayList(); for (FlattenInstruction instruction : flattenInstructions) { //System.out.println("exclude " + instruction.path); excludes.add(instruction.path); } // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Map flattenedSessions = new HashMap(noOfFlattenedInstances); flattenInstances (similarTasks.getLeftHandSide(), flattenInstructions, flattenedSessions, excludes); flattenInstances (similarTasks.getRightHandSide(), flattenInstructions, flattenedSessions, excludes); // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>> /*for (IUserSession session : flattenedSessions.values()) { System.out.println("user session {"); for (ITaskInstance instance : session) { System.out.println(instance); } System.out.println("}"); }*/ // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< List flattenedSessionList = new LinkedList(flattenedSessions.values()); new SequenceForTaskDetectionRule (TaskEquality.IDENTICAL, taskFactory, taskBuilder).apply(flattenedSessionList); Map replacements = new HashMap(); ITask replacementTask = null; for (Map.Entry entry : flattenedSessions.entrySet()) { // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>> // the user sessions were sufficiently equal to have now only one common task as child if (entry.getValue().size() != 1) { //new TaskTreeEncoder().encode(entry.getValue(), System.out, excludes); throw new RuntimeException("flattened sessions were not combined as expected"); } if (replacementTask == null) { replacementTask = entry.getValue().get(0).getTask(); } else if (replacementTask != entry.getValue().get(0).getTask()) { throw new RuntimeException("two separate replacement tasks were calculated as " + "replacement, one for both tasks to be merged"); } // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< replacements.put(entry.getKey(), entry.getValue().get(0)); } // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // ((Task) replacementTask).setDescription // (replacementTask + " calculated as full replacement for " + // similarTasks.getLeftHandSide() + " and " + similarTasks.getRightHandSide()); int allInstances = similarTasks.getLeftHandSide().getInstances().size() + similarTasks.getRightHandSide().getInstances().size(); if (replacements.size() != allInstances) { throw new RuntimeException("number of replacements does not match number of instances"); } /*if (replacements.size() > 0) { System.out.println("replacement task is calculated to be: "); new TaskTreeEncoder().encode(replacements.values().iterator().next().getTask(), System.out); }*/ // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // create also replacements for potential parent iterations or optionals IIteration harmonizedIteration = taskFactory.createNewIteration(); taskBuilder.setMarkedTask(harmonizedIteration, replacementTask); IOptional harmonizedOptional = taskFactory.createNewOptional(); taskBuilder.setMarkedTask(harmonizedOptional, replacementTask); for (IUserSession session : appData.getSessions()) { replaceTaskInstances(session, replacements, similarTasks, harmonizedIteration, harmonizedOptional); // several subsequent instances, which had formerly different tasks, may now have the // same. Hence, they need to be merged. But as everything else would be way too complex, // we only perform the merge, if they occur next to each other on the same level mergeSubsequentIdenticalMarkingTemporalRelationships(session); } // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> if (replacements.size() != 0) { for (Map.Entry entry : replacements.entrySet()) { System.out.println ("did not replace instance " + entry.getKey() + " with " + entry.getValue()); } throw new RuntimeException(); } // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< } /** * */ private List getFlattenInstructions(SimilarTasks similarTasks, RuleApplicationData appData) { TaskTraversal leftHandSideTraversal = similarTasks.getLeftHandSideTraversal(); TaskTraversal rightHandSideTraversal = similarTasks.getRightHandSideTraversal(); List result = new LinkedList(); TaskComparator comp = identTaskHandlStrat.getTaskComparator(); // first create instructions for the deltas for (Delta delta : similarTasks.getPatch().getDeltas()) { if ((delta.getType() == Delta.TYPE.INSERT) || (delta.getType() == Delta.TYPE.DELETE)) { // System.out.println("handling " + delta.getType()); Chunk chunk; TaskPath insertAfterPath = null; TaskPath insertBeforePath; if (delta.getType() == Delta.TYPE.INSERT) { chunk = delta.getRevised(); int pos = delta.getOriginal().getPosition(); // System.out.println(" position " + pos); if (pos > 0) { insertAfterPath = leftHandSideTraversal.getTraversalPaths()[pos - 1]; } insertBeforePath = leftHandSideTraversal.getTraversalPaths()[pos]; } else { chunk = delta.getOriginal(); int pos = delta.getRevised().getPosition(); if (pos > 0) { insertAfterPath = rightHandSideTraversal.getTraversalPaths()[pos - 1]; } insertBeforePath = rightHandSideTraversal.getTraversalPaths()[pos]; } ITask child = getTaskForChunk(chunk, appData); IOptional optional; if (child instanceof IOptional) { optional = (IOptional) child; } else { optional = taskFactory.createNewOptional(); taskBuilder.setMarkedTask(optional, child); // ((Task) optional).setDescription(optional.getDescription() + // " created for " + delta.getType()); optional = (IOptional) appData.ensureUnique(optional); createReplacementInstructions(chunk, optional, null, result); } // create a flatten instruction for the non-occurrence of the optional result.add(new FlattenInstruction(insertAfterPath, insertBeforePath, optional)); } else if (delta.getType() == Delta.TYPE.CHANGE) { ITask child1; ITask child2; // check, if the change covers the whole traversal. If so reuse the root task. // Otherwise, create intermediate tasks if required representing both sides // of changes if ((delta.getOriginal().getPosition() == 0) && (delta.getOriginal().size() == similarTasks.getLeftHandSideTraversal().size())) { child1 = similarTasks.getLeftHandSide(); } else { child1 = getTaskForChunk(delta.getOriginal(), appData); } if ((delta.getRevised().getPosition() == 0) && (delta.getRevised().size() == similarTasks.getRightHandSideTraversal().size())) { child2 = similarTasks.getRightHandSide(); } else { child2 = getTaskForChunk(delta.getRevised(), appData); } // check if either of the variants is an iteration or optional of the respective // other variant. If so, ensure, that the iteration or optional are preserved ITask markedTask1 = (child1 instanceof IMarkingTemporalRelationship) ? ((IMarkingTemporalRelationship) child1).getMarkedTask() : null; ITask markedTask2 = (child2 instanceof IMarkingTemporalRelationship) ? ((IMarkingTemporalRelationship) child2).getMarkedTask() : null; if (comp.equals(markedTask1, child2)) { if (child1 instanceof IOptional) { createReplacementInstructions (delta.getRevised(), (IOptional) child1, null, result); } else { throw new java.lang.UnsupportedOperationException("not implemented yet"); } } else if (comp.equals(markedTask2, child1)) { if (child2 instanceof IOptional) { createReplacementInstructions (delta.getOriginal(), (IOptional) child2, null, result); } else { throw new java.lang.UnsupportedOperationException("not implemented yet"); } } else if ((markedTask1 != null) && (comp.equals(markedTask1, markedTask2))) { throw new java.lang.UnsupportedOperationException("not implemented yet"); } else { // its time to create a selection of both variants. If one is already // a selection, it is reused and extended, if required ITask expectedChild1 = null; ITask expectedChild2 = null; ISelection selection = null; if (child1 instanceof ISelection) { selection = (ISelection) child1; } if (child2 instanceof ISelection) { if (selection == null) { // only child 2 is a selection --> extend it with child 1 selection = (ISelection) child2; addSelectionChildIfRequired(selection, child1); expectedChild1 = child1; } else { // both are selections --> extend child 1 with variants of child2 for (ITask child : ((ISelection) child2).getChildren()) { addSelectionChildIfRequired(selection, child); } } } else if (selection != null) { // only child 1 is a selection --> extend it with child 2 addSelectionChildIfRequired(selection, child2); expectedChild2 = child2; } else if (selection == null) { // none of both is already a selection, so create a new one with both // of the children as variants selection = taskFactory.createNewSelection(); // ((Task) selection).setDescription(selection.getDescription() + // " created for " + delta.getType()); taskBuilder.addChild(selection, child1); taskBuilder.addChild(selection, child2); expectedChild1 = child1; expectedChild2 = child2; } selection = (ISelection) appData.ensureUnique(selection); createReplacementInstructions (delta.getOriginal(), selection, expectedChild1, result); createReplacementInstructions (delta.getRevised(), selection, expectedChild2, result); } } } // create instructions to harmonize marking temporal relationships for untraversed tasks int leftHandSideIndex = 0; int rightHandSideIndex = 0; OUTER: while (leftHandSideIndex < leftHandSideTraversal.getTraversalPaths().length) { for (Delta delta : similarTasks.getPatch().getDeltas()) { if (delta.getOriginal().getPosition() == leftHandSideIndex) { if (delta.getType() == Delta.TYPE.INSERT) { rightHandSideIndex += delta.getRevised().size(); // do not continue OUTER to ensure, that the left hand side and the // right hand side coming directly after the insert are handled } else if (delta.getType() == Delta.TYPE.DELETE) { leftHandSideIndex += delta.getOriginal().size(); continue OUTER; } else if (delta.getType() == Delta.TYPE.CHANGE) { leftHandSideIndex += delta.getOriginal().size(); rightHandSideIndex += delta.getRevised().size(); continue OUTER; } } } TaskPath leftPath = leftHandSideTraversal.getTraversalPaths()[leftHandSideIndex]; TaskPath rightPath = rightHandSideTraversal.getTraversalPaths()[rightHandSideIndex]; if (comp.equals(leftPath.getLast(), rightPath.getLast())) { if (leftPath.getTask(leftPath.size() - 2) instanceof IOptional) { IOptional optional = (IOptional) leftPath.getTask(leftPath.size() - 2); result.add (new FlattenInstruction(leftPath.subPath(0, leftPath.size()), optional)); result.add(new FlattenInstruction(rightPath, optional)); } else if (rightPath.getTask(rightPath.size() - 2) instanceof IOptional) { IOptional optional = (IOptional) rightPath.getTask(rightPath.size() - 2); result.add (new FlattenInstruction(rightPath.subPath(0, rightPath.size()), optional)); result.add(new FlattenInstruction(leftPath, optional)); } } leftHandSideIndex++; rightHandSideIndex++; } // then create instructions for what stays the same OUTER: for (TaskPath path : leftHandSideTraversal.getTraversalPaths()) { for (FlattenInstruction instruction : result) { if (instruction.matches(path)) { continue OUTER; } } result.add(new FlattenInstruction(path)); } OUTER: for (TaskPath path : rightHandSideTraversal.getTraversalPaths()) { for (FlattenInstruction instruction : result) { if (instruction.matches(path)) { continue OUTER; } } result.add(new FlattenInstruction(path)); } return result; } /** * */ private void addSelectionChildIfRequired(ISelection selection, ITask child) { for (ITask candidate : selection.getChildren()) { if (identTaskHandlStrat.getTaskComparator().equals(candidate, child)) { return; } } taskBuilder.addChild(selection, child); } /** * */ private ITask getTaskForChunk(Chunk chunk, RuleApplicationData appData) { if (chunk.size() == 1) { TaskPath path = (TaskPath) chunk.getLines().get(0); return path.getTask(path.size() - 1); } else { ISequence task = taskFactory.createNewSequence(); // ((Task) task).setDescription(task.getDescription() + // " created to represent a chunk"); for (Object pathObj : chunk.getLines()) { TaskPath path = (TaskPath) pathObj; taskBuilder.addChild(task, path.getLast()); } return appData.ensureUnique(task); } } /** * */ private void createReplacementInstructions(Chunk chunk, ITask replacement, ITask selectedChild, List result) { // create a flatten instruction for the occurrence of the replacement for (Object pathObj : chunk.getLines()) { TaskPath path = (TaskPath) pathObj; if (replacement instanceof IOptional) { result.add(new FlattenInstruction(path, (IOptional) replacement)); } else if (replacement instanceof ISelection) { result.add (new FlattenInstruction(path, (ISelection) replacement, selectedChild)); } } } /** * */ private void flattenInstances(ITask task, List flattenInstructions, Map flattenedSessions, List excludes) { //int i = 0; for (ITaskInstance instance : task.getInstances()) { /*System.out.println("flattening instance " + i++ + " of task " + task); new TaskTreeEncoder().encode(instance, System.out, excludes);*/ TaskPath taskPath = new TaskPath(); taskPath.add(instance.getTask(), 0); IUserSession session = taskFactory.createUserSession(); flattenInstance(instance, taskPath, flattenInstructions, session, new LinkedList()); //new TaskTreeEncoder().encode(session, System.out, excludes); flattenedSessions.put(instance, session); } } /** * */ private void flattenInstance(ITaskInstance instance, TaskPath taskPath, List flattenInstructions, IUserSession session, List previousPaths) { boolean instructionApplied = false; TaskComparator comp = identTaskHandlStrat.getTaskComparator(); //System.out.println("applying instructions on " + taskPath); for (FlattenInstruction instruction : flattenInstructions) { if (instruction.matches(taskPath)) { //System.out.print("found instruction "); //instruction.dump(System.out); switch (instruction.getInstruction()) { case DONT_TRAVERSE: { if (instance != null) { taskBuilder.addTaskInstance(session, instance); } instructionApplied = true; break; } case MAKE_OPTIONAL: { instructionApplied = true; if (instance == null) { break; } IOptional optional = instruction.getOptional(); if (comp.equals(optional, instance.getTask())) { // the instance is already an instance of the correct optional --> reuse taskBuilder.addTaskInstance(session, instance); } else if (comp.equals(optional.getMarkedTask(), instance.getTask())) { IOptionalInstance optionalInstance = taskFactory.createNewTaskInstance(optional); taskBuilder.setChild(optionalInstance, instance); taskBuilder.addTaskInstance(session, optionalInstance); } else if (optional.getMarkedTask() instanceof ISequence) { // first check, if the instance was already created, if not create it ISequenceInstance sequenceInstance = ensureSequenceChildInstanceFor (optional, (ISequence) optional.getMarkedTask(), session); taskBuilder.addChild(sequenceInstance, instance); } break; } case MAKE_SELECTION: { instructionApplied = true; if (instance == null) { break; } ISelection selection = instruction.getSelection(); if (comp.equals(instruction.getSelectedChild(), instance.getTask())) { ISelectionInstance selectionInstance = taskFactory.createNewTaskInstance(selection); taskBuilder.setChild(selectionInstance, instance); taskBuilder.addTaskInstance(session, selectionInstance); } else if (instruction.getSelectedChild() == null) { // both variants were already selections. They will have been merged. // Hence we can reuse the child instances of the existing selection // instances. if (comp.equals(instance.getTask(), selection)) { taskBuilder.addTaskInstance(session, instance); } else { ISelectionInstance selectionInstance = taskFactory.createNewTaskInstance(selection); taskBuilder.setChild(selectionInstance, ((ISelectionInstance) instance).getChild()); taskBuilder.addTaskInstance(session, selectionInstance); taskBuilder.discardTaskInstance(instance); } } else if (instruction.getSelectedChild() instanceof ISequence) { // first check, if the instance was already created, if not create it ISequenceInstance sequenceInstance = ensureSequenceChildInstanceFor (selection, (ISequence) instruction.getSelectedChild(), session); taskBuilder.addChild(sequenceInstance, instance); } break; } case INTEGRATE_OPTIONAL: { TaskPath previousPath = previousPaths.size() > 0 ? previousPaths.get(previousPaths.size() - 1) : null; if (pathsMatch(instruction.getPrecedingPath(), previousPath)) { IOptional optional = instruction.getOptional(); IOptionalInstance optionalInstance = taskFactory.createNewTaskInstance(optional); taskBuilder.addTaskInstance(session, optionalInstance); } if (instance != null) { taskBuilder.addTaskInstance(session, instance); } instructionApplied = true; break; } default : { throw new RuntimeException("unknown instruction type"); } } } if (instructionApplied) { break; } } if (!instructionApplied) { ITask task = taskPath.getLast(); if (task instanceof IIteration) { taskPath.add(((IIteration) task).getMarkedTask(), 0); if (instance != null) { for (ITaskInstance child : (IIterationInstance) instance) { flattenInstance (child, taskPath, flattenInstructions, session, previousPaths); } } else { flattenInstance(null, taskPath, flattenInstructions, session, previousPaths); } taskPath.removeLast(); } else if (task instanceof ISequence) { List children = ((ISequence) task).getChildren(); for (int i = 0; i < children.size(); i++) { ITaskInstance child = null; if (instance != null) { child = ((ISequenceInstance) instance).get(i); } taskPath.add(children.get(i), i); flattenInstance (child, taskPath, flattenInstructions, session, previousPaths); taskPath.removeLast(); } } else if (task instanceof ISelection) { if (instance != null) { taskPath.add(((ISelectionInstance) instance).getChild().getTask(), 0); flattenInstance(((ISelectionInstance) instance).getChild(), taskPath, flattenInstructions, session, previousPaths); taskPath.removeLast(); } else { // check if the selection has any child for which a rule must be considered List children = ((ISelection) task).getChildren(); for (int i = 0; i < children.size(); i++) { taskPath.add(children.get(i), i); flattenInstance (null, taskPath, flattenInstructions, session, previousPaths); taskPath.removeLast(); } } } else if (task instanceof IOptional) { taskPath.add(((IOptional) task).getMarkedTask(), 0); ITaskInstance child = null; if (instance != null) { child = ((IOptionalInstance) instance).getChild(); } flattenInstance(child, taskPath, flattenInstructions, session, previousPaths); taskPath.removeLast(); if ((instance != null) && (child == null)) { // add the empty optional instance taskBuilder.addTaskInstance(session, instance); previousPaths.add(new TaskPath(taskPath)); } } else if (instance != null) { taskBuilder.addTaskInstance(session, instance); previousPaths.add(new TaskPath(taskPath)); } } else { previousPaths.add(new TaskPath(taskPath)); } } /** * */ private ISequenceInstance ensureSequenceChildInstanceFor(ITask replacement, ISequence expectedChildSequence, IUserSession session) { // first check, if there is already an instance of the expected sequence. If so, check if // its child is a sequence instance. If not create a new task instance with an appropriate // sequence instance ITaskInstance prevInstance = session.size() > 0 ? session.get(session.size() - 1) : null; ISequenceInstance sequenceInstance = null; if ((prevInstance != null) && (identTaskHandlStrat.getTaskComparator().equals(prevInstance.getTask(), replacement))) { if ((prevInstance instanceof IOptionalInstance) && (((IOptionalInstance) prevInstance).getChild() instanceof ISequenceInstance)) { sequenceInstance = (ISequenceInstance) ((IOptionalInstance) prevInstance).getChild(); } else if ((prevInstance instanceof ISelectionInstance) && (((ISelectionInstance) prevInstance).getChild() instanceof ISequenceInstance)) { sequenceInstance = (ISequenceInstance) ((ISelectionInstance) prevInstance).getChild(); } } // although we found a sequence instance as expected, this instance might already be fully // created and a new (iterated) one must be created. Check for it and handle correctly if ((sequenceInstance != null) && (sequenceInstance.size() == expectedChildSequence.getChildren().size())) { sequenceInstance = null; } if (sequenceInstance == null) { //System.out.println("creating new sequence instance of selected child"); sequenceInstance = taskFactory.createNewTaskInstance(expectedChildSequence); ITaskInstance replacementInstance = null; if (replacement instanceof IOptional) { replacementInstance = taskFactory.createNewTaskInstance((IOptional) replacement); taskBuilder.setChild((IOptionalInstance) replacementInstance, sequenceInstance); } else if (replacement instanceof ISelection) { /*System.out.println("replacement: "); new TaskTreeEncoder().encode(replacement, System.out); System.out.println("expectedChild: "); new TaskTreeEncoder().encode(expectedChildSequence, System.out);*/ replacementInstance = taskFactory.createNewTaskInstance((ISelection) replacement); taskBuilder.setChild((ISelectionInstance) replacementInstance, sequenceInstance); } taskBuilder.addTaskInstance(session, replacementInstance); } return sequenceInstance; } /** * */ private void replaceTaskInstances(ITaskInstanceList taskInstanceList, Map replacements, SimilarTasks similarTasks, IIteration harmonizedIteration, IOptional harmonizedOptional) { for (int i = 0; i < taskInstanceList.size(); i++) { ITaskInstance childInstance = taskInstanceList.get(i); ITaskInstance replacement = replacements.remove(childInstance); if (replacement != null) { // update the model for sequences (others are updated in the calling method) if (taskInstanceList instanceof ISequenceInstance) { ISequence task = ((ISequenceInstance) taskInstanceList).getSequence(); taskBuilder.setChild(task, i, replacement.getTask()); } // perform the actual replacement and throw away the instance taskBuilder.setTaskInstance(taskInstanceList, i, replacement); TaskPath path = new TaskPath(); path.add(childInstance.getTask(), 0); discardTaskInstancesNotBelongingToTraversals(childInstance, path, similarTasks); } else { ITask modelUpdate = replaceTaskInstances(childInstance, replacements, similarTasks, harmonizedIteration, harmonizedOptional); if (modelUpdate != null) { if (taskInstanceList instanceof ISequenceInstance) { taskBuilder.setChild (((ISequenceInstance) taskInstanceList).getSequence(), i, modelUpdate); } } } } } /** * */ private void replaceTaskInstances(IOptionalInstance optionalInstance, Map replacements, SimilarTasks similarTasks, IIteration harmonizedIteration, IOptional harmonizedOptional) { ITaskInstance childInstance = optionalInstance.getChild(); if (childInstance != null) { ITaskInstance replacement = replacements.remove(childInstance); if (replacement != null) { // do not update the model --> is updated in the calling method // perform the actual replacement and throw away the instance taskBuilder.setChild(optionalInstance, replacement); TaskPath path = new TaskPath(); path.add(childInstance.getTask(), 0); discardTaskInstancesNotBelongingToTraversals(childInstance, path, similarTasks); } else { ITask modelUpdate = replaceTaskInstances(childInstance, replacements, similarTasks, harmonizedIteration, harmonizedOptional); if (modelUpdate != null) { taskBuilder.setMarkedTask(optionalInstance.getOptional(), modelUpdate); } } } } /** * */ private void replaceTaskInstances(ISelectionInstance selectionInstance, Map replacements, SimilarTasks similarTasks, IIteration harmonizedIteration, IOptional harmonizedOptional) { TaskComparator comparator = identTaskHandlStrat.getTaskComparator(); ITaskInstance childInstance = selectionInstance.getChild(); if (childInstance != null) { ITaskInstance replacement = replacements.remove(childInstance); if (replacement != null) { if (replacement instanceof ISelectionInstance) { taskBuilder.discardTaskInstance(replacement); replacement = ((ISelectionInstance) replacement).getChild(); } // update the model // we replace all instances of the merged tasks with instances of a new task. // hence, we also have to remove the task of the replaced children from the // available variants of the selection taskBuilder.removeChild(selectionInstance.getSelection(), childInstance.getTask()); boolean found = false; for (ITask child : selectionInstance.getSelection().getChildren()) { if (comparator.equals(child, replacement.getTask())) { found = true; break; } } if (!found) { taskBuilder.addChild(selectionInstance.getSelection(), replacement.getTask()); } // perform the actual replacement and throw away the instance taskBuilder.setChild(selectionInstance, replacement); TaskPath path = new TaskPath(); path.add(childInstance.getTask(), 0); discardTaskInstancesNotBelongingToTraversals(childInstance, path, similarTasks); } else { ITask modelUpdate = replaceTaskInstances(childInstance, replacements, similarTasks, harmonizedIteration, harmonizedOptional); if (modelUpdate != null) { taskBuilder.removeChild (selectionInstance.getSelection(), childInstance.getTask()); boolean found = false; for (ITask child : selectionInstance.getSelection().getChildren()) { if (comparator.equals(child, modelUpdate)) { found = true; break; } } if (!found) { taskBuilder.addChild(selectionInstance.getSelection(), modelUpdate); } } } } } /** * */ private ITask replaceTaskInstances(ITaskInstance childInstance, Map replacements, SimilarTasks similarTasks, IIteration harmonizedIteration, IOptional harmonizedOptional) { ITask modelUpdate = null; if (childInstance instanceof IIterationInstance) { ITask markedTask = ((IIterationInstance) childInstance).getIteration().getMarkedTask(); if ((markedTask == similarTasks.getLeftHandSide()) || (markedTask == similarTasks.getRightHandSide())) { taskBuilder.setTask(childInstance, harmonizedIteration); modelUpdate = harmonizedIteration; } replaceTaskInstances((ITaskInstanceList) childInstance, replacements, similarTasks, harmonizedIteration, harmonizedOptional); } else if (childInstance instanceof IOptionalInstance) { ITask markedTask = ((IOptionalInstance) childInstance).getOptional().getMarkedTask(); if ((markedTask == similarTasks.getLeftHandSide()) || (markedTask == similarTasks.getRightHandSide())) { taskBuilder.setTask(childInstance, harmonizedOptional); modelUpdate = harmonizedOptional; } replaceTaskInstances((IOptionalInstance) childInstance, replacements, similarTasks, harmonizedIteration, harmonizedOptional); } else if (childInstance instanceof ISelectionInstance) { replaceTaskInstances((ISelectionInstance) childInstance, replacements, similarTasks, harmonizedIteration, harmonizedOptional); } else if (childInstance instanceof ISequenceInstance) { replaceTaskInstances((ITaskInstanceList) childInstance, replacements, similarTasks, harmonizedIteration, harmonizedOptional); } return modelUpdate; } /** * */ private void discardTaskInstancesNotBelongingToTraversals(ITaskInstance instance, TaskPath pathToInstance, SimilarTasks similarTasks) { boolean discarded = false; for (TaskPath candidate : similarTasks.getLeftHandSideTraversal().getTraversalPaths()) { if (candidate.size() > pathToInstance.size()) { TaskPath potentialEqualSubPath = candidate.subPath(0, pathToInstance.size()); if (pathsMatch(potentialEqualSubPath, pathToInstance)) { taskBuilder.discardTaskInstance(instance); discarded = true; break; } } } if (!discarded) { for (TaskPath candidate : similarTasks.getRightHandSideTraversal().getTraversalPaths()) { if (candidate.size() > pathToInstance.size()) { TaskPath potentialEqualSubPath = candidate.subPath(0, pathToInstance.size()); if (pathsMatch(potentialEqualSubPath, pathToInstance)) { taskBuilder.discardTaskInstance(instance); discarded = true; break; } } } } if (discarded) { // now also discard the children if (instance instanceof ISequenceInstance) { int index = 0; for (ITaskInstance childInstance : (ITaskInstanceList) instance) { pathToInstance.add(childInstance.getTask(), index++); discardTaskInstancesNotBelongingToTraversals (childInstance, pathToInstance, similarTasks); pathToInstance.removeLast(); } } else if (instance instanceof IIterationInstance) { for (ITaskInstance childInstance : (ITaskInstanceList) instance) { pathToInstance.add(childInstance.getTask(), 0); discardTaskInstancesNotBelongingToTraversals (childInstance, pathToInstance, similarTasks); pathToInstance.removeLast(); } } else if (instance instanceof IOptionalInstance) { ITaskInstance childInstance = ((IOptionalInstance) instance).getChild(); if (childInstance != null) { pathToInstance.add(childInstance.getTask(), 0); discardTaskInstancesNotBelongingToTraversals (childInstance, pathToInstance, similarTasks); pathToInstance.removeLast(); } } else if (instance instanceof ISelectionInstance) { ITaskInstance childInstance = ((ISelectionInstance) instance).getChild(); pathToInstance.add(childInstance.getTask(), 0); discardTaskInstancesNotBelongingToTraversals (childInstance, pathToInstance, similarTasks); pathToInstance.removeLast(); } } } /** * */ private void mergeSubsequentIdenticalMarkingTemporalRelationships(ITaskInstanceList list) { int index = 0; TaskComparator comparator = identTaskHandlStrat.getTaskComparator(); while (index < (list.size() - 1)) { ITaskInstance instance1 = list.get(index); ITaskInstance instance2 = list.get(index + 1); if (comparator.equals(instance1.getTask(), instance2.getTask())) { if (instance1 instanceof IIterationInstance) { // add the children of the second to the first iteration instance and discard // the second for (ITaskInstance child : (IIterationInstance) instance2) { taskBuilder.addChild((IIterationInstance) instance1, child); } taskBuilder.removeTaskInstance(list, index + 1); taskBuilder.discardTaskInstance(instance2); } else if (instance1 instanceof IOptionalInstance) { ITaskInstance optionalChildInstance1 = ((IOptionalInstance) instance1).getChild(); ITaskInstance optionalChildInstance2 = ((IOptionalInstance) instance2).getChild(); if (optionalChildInstance1 == null) { // independent of the second, just remove the first. The second will be the // unique representation taskBuilder.removeTaskInstance(list, index); } else if (optionalChildInstance2 == null) { // remove the second. The first will be the unique representation taskBuilder.removeTaskInstance(list, index + 1); } else if (optionalChildInstance1 instanceof IIterationInstance) { // add all children of the second optional iteration instance to the // first and discard the second for (ITaskInstance child : (IIterationInstance) optionalChildInstance2) { taskBuilder.addChild ((IIterationInstance) optionalChildInstance1, child); } taskBuilder.removeTaskInstance(list, index + 1); taskBuilder.discardTaskInstance(instance2); } else { // both optional children are no iterations --> create an iteration // for them and add them both as children. throw new java.lang.UnsupportedOperationException("not implemented yet"); } } else { index++; } } else { index++; } } for (ITaskInstance child : list) { if (child instanceof ITaskInstanceList) { mergeSubsequentIdenticalMarkingTemporalRelationships((ITaskInstanceList) child); } } } /** * */ private static boolean pathsMatch(TaskPath path1, TaskPath path2) { if (path1 == null) { return path2 == null; } else if ((path2 != null) && (path1.size() == path2.size())) { for (int i = 0; i < path1.size(); i++) { if (!path1.get(i).equals(path2.get(i))) { return false; } } return true; } else { return false; } } /** * */ /*private boolean containsNewTask(ITask task, RuleApplicationData appData) { if (appData.isSelfCreatedTask(task)) { return true; } else if (task instanceof IStructuringTemporalRelationship) { for (ITask child : ((IStructuringTemporalRelationship) task).getChildren()) { if (containsNewTask(child, appData)) { return true; } } } else if (task instanceof IMarkingTemporalRelationship) { return containsNewTask(((IMarkingTemporalRelationship) task).getMarkedTask(), appData); } return false; }*/ /** * */ private class RuleApplicationData { /** * */ private List sessions; /** * */ private ITaskModel taskModel; /** * */ private MostSimilarTaskDeterminer mostSimilarTasksDeterminer = new MostSimilarTaskDeterminer(identTaskHandlStrat.getTaskComparator()); /** * */ private List mostSimilarTasks; /** * */ private List newTasks = new LinkedList(); /** * */ private RuleApplicationResult ruleApplicationResult = new RuleApplicationResult(); /** * */ private RuleApplicationData(List sessions) { this.sessions = sessions; } /** * */ boolean isSelfCreatedTask(ITask task) { for (ITask candidate : newTasks) { if (candidate == task) { return true; } } return false; } /** * @return the mostSimilarTasksDeterminer */ private MostSimilarTaskDeterminer getMostSimilarTasksDeterminer() { return mostSimilarTasksDeterminer; } /** * */ private void markAsAlreadyCondensed(ITask task1, ITask task2) { mostSimilarTasksDeterminer.addComparisonToSkip(task1, task2); } /** * */ private RuleApplicationResult finalizeRuleApplicationResult() { for (ITask newTask : newTasks) { ruleApplicationResult.addNewlyCreatedTask(newTask); if (newTask instanceof IOptional) { if (isSelfCreatedTask(((IOptional) newTask).getMarkedTask()) && (((IOptional) newTask).getMarkedTask() instanceof ISequence)) { ruleApplicationResult.addNewlyCreatedTask (((IOptional) newTask).getMarkedTask()); } } else if (newTask instanceof ISelection) { for (ITask child : ((ISelection) newTask).getChildren()) { if (isSelfCreatedTask(child) && (child instanceof ISequence)) { ruleApplicationResult.addNewlyCreatedTask(child); } } } } return ruleApplicationResult; } /** * @return the tree */ private List getSessions() { return sessions; } /** * @return the taskModel */ private ITaskModel getTaskModel() { return taskModel; } /** * @param taskModel the taskModel to set */ private void setTaskModel(ITaskModel taskModel) { this.taskModel = taskModel; } /** * @return the orderedDiffLevels */ private List getMostSimilarTasks() { return mostSimilarTasks; } /** * @param orderedDiffLevels the orderedDiffLevels to set */ private void setMostSimilarTasks(List mostSimilarTasks) { this.mostSimilarTasks = mostSimilarTasks; } /** * */ private ITask ensureUnique(ITask task) { // replacements done in this rule are either optionals or selections. So focus on them if (task instanceof IOptional) { for (ITask newTask : newTasks) { if (newTask instanceof IOptional) { ITask child1 = ((IOptional) task).getMarkedTask(); ITask child2 = ((IOptional) newTask).getMarkedTask(); if (createdChildEquals(child1, child2)) { // System.out.println("reusing optional " + newTask + " for " + task); return newTask; } } } } else if (task instanceof ISelection) { for (ITask newTask : newTasks) { if (newTask instanceof ISelection) { List children1 = ((ISelection) task).getChildren(); List children2 = ((ISelection) newTask).getChildren(); if (createdSelectionChildrenEqual(children1, children2)) { /*System.out.println("reusing selection " + newTask + " for " + task); System.out.println("---------------------------- existing task"); new TaskTreeEncoder().encode(newTask, System.out); System.out.println("---------------------------- completely new task"); new TaskTreeEncoder().encode(task, System.out);*/ return newTask; } } } } else if (task instanceof ISequence) { List allRelevant = new ArrayList<>(); for (ITask candidate : newTasks) { if (candidate instanceof ISequence) { allRelevant.add((ISequence) candidate); } } for (ITask candidate : taskModel.getTasks()) { if (candidate instanceof ISequence) { allRelevant.add((ISequence) candidate); } } for (ISequence candidate : allRelevant) { List children1 = ((ISequence) task).getChildren(); List children2 = ((ISequence) candidate).getChildren(); boolean equal = false; if (children1.size() == children2.size()) { equal = true; for (int i = 0; i < children1.size(); i++) { if (children1.get(i) != children2.get(i)) { equal = false; break; } } } if (equal) { // System.out.println("reusing sequence " + candidate + " for " + task); return candidate; } } } newTasks.add(task); return task; } /** * */ private boolean createdSelectionChildrenEqual(List children1, List children2) { if (children1.size() != children2.size()) { return false; } for (ITask child1 : children1) { boolean found = false; for (ITask child2 : children2) { if (createdChildEquals(child1, child2)) { found = true; break; } } if (!found) { return false; } } return true; } /** * */ private boolean createdChildEquals(ITask child1, ITask child2) { if (child1 == child2) { return true; } else if (!isSelfCreatedTask(child1) && !isSelfCreatedTask(child2)) { // the simple comparison can not work if the tasks are not self created return false; } else if ((child1 instanceof ISequence) && (child2 instanceof ISequence)) { ISequence sequence1 = (ISequence) child1; ISequence sequence2 = (ISequence) child2; if (sequence1.getChildren().size() != sequence2.getChildren().size()) { return false; } for (int i = 0; i < sequence1.getChildren().size(); i++) { if (sequence1.getChildren().get(i) != sequence2.getChildren().get(i)) { return false; } } return true; } else { return false; } } } /** * */ private static class FlattenInstruction { /** * */ private enum Instruction { DONT_TRAVERSE, MAKE_OPTIONAL, MAKE_SELECTION, INTEGRATE_OPTIONAL }; /** */ private TaskPath path; /** */ private TaskPath precedingPath; /** */ private Instruction instruction; /** */ private IOptional optional = null; /** */ private ISelection selection = null; /** */ private ITask selectedChild = null; /** * */ private FlattenInstruction(TaskPath path) { this.path = path; this.instruction = Instruction.DONT_TRAVERSE; } /** * */ private FlattenInstruction(TaskPath path, IOptional optional) { this.path = path; this.instruction = Instruction.MAKE_OPTIONAL; this.optional = optional; } /** * */ private FlattenInstruction(TaskPath path, ISelection selection, ITask selectedChild) { this.path = path; this.instruction = Instruction.MAKE_SELECTION; this.selection = selection; this.selectedChild = selectedChild; } /** * */ private FlattenInstruction(TaskPath precedingPath, TaskPath path, IOptional optional) { this.path = path; this.precedingPath = precedingPath; this.instruction = Instruction.INTEGRATE_OPTIONAL; this.optional = optional; } /** * */ IOptional getOptional() { return optional; } /** * @return the selection */ ISelection getSelection() { return selection; } /** * @return the selectedChild */ ITask getSelectedChild() { return selectedChild; } /** * @return the instruction */ Instruction getInstruction() { return instruction; } /** * @return the precedingPath */ TaskPath getPrecedingPath() { return precedingPath; } /** * */ boolean matches(TaskPath path) { return pathsMatch(this.path, path); } // /** // * // */ // void dump(PrintStream out) { // for (int i = 0; i < path.size(); i++) { // out.print("-->"); // out.print(path.getTask(i).getId()); // out.print('('); // out.print(path.get(i).getIndex()); // out.print(')'); // } // // out.print(" "); // out.print(instruction); // // out.println(); // } } }