source: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/CondenseSimilarTasksRule.java

Last change on this file was 2132, checked in by pharms, 8 years ago
  • added possibility to select the minimal amount of action instances a detected sequence must cover
File size: 101.8 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
16
17import java.util.ArrayList;
18import java.util.Collection;
19import java.util.HashMap;
20import java.util.Iterator;
21import java.util.LinkedList;
22import java.util.List;
23import java.util.Map;
24
25import de.ugoe.cs.autoquest.tasktrees.taskequality.TaskEquality;
26import de.ugoe.cs.autoquest.tasktrees.temporalrelation.utils.MostSimilarTaskDeterminer;
27import de.ugoe.cs.autoquest.tasktrees.temporalrelation.utils.SimilarTasks;
28import de.ugoe.cs.autoquest.tasktrees.temporalrelation.utils.TaskTraversal;
29import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor;
30import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
31import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
32import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance;
33import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship;
34import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptional;
35import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptionalInstance;
36import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
37import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance;
38import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
39import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance;
40import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
41import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskBuilder;
42import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskFactory;
43import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
44import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstanceList;
45import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
46import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
47import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskPath;
48import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskTreeUtils;
49import de.ugoe.cs.util.console.Console;
50import difflib.Chunk;
51import difflib.Delta;
52
53/**
54 * <p>
55 * This rule performs a condensing of a task tree. It searches for similar tasks and merges them
56 * if possible in to one task with different execution variants. Hence, this rule detects selections
57 * and optionals.
58 * </p>
59 * <p>
60 * The similarity of tasks is determined by comparing task traversals. A task traversal is an
61 * ordered list of the leaf nodes of a task. This is similar to performing a minimal execution of
62 * the task. The task traversals of two tasks are compared using string comparison algorithms. The
63 * less differences the lists have, the more similar they are.
64 * </p>
65 * <p>
66 * If two tasks are similar, they are merged. Merging is done based on the differences between
67 * the task traversals. Two tasks are merged based on their instances. First, all instances of both
68 * tasks are flattened. Instances of selections or commonalities of both tasks stay unflattened.
69 * Then the lists resulting from this flattening are extended with instances of optionals and
70 * selections which are introduced where required. Finally, the instances are put together to a
71 * single task again by applying the {@link SequenceForTaskDetectionRule} on them.
72 * </p>
73 * <p>
74 * Merging has to consider several specific situations. E.g., tasks may look similar but due to
75 * iterations, they can not be merged correctly. Here, the rule ensures, that these so called
76 * interleaving iterations are detected, not traversed when traversing a task and its instances,
77 * and, therefore, preserved.
78 * </p>
79 * <p>
80 * All details about this rule are described in the extended ACHI2014 paper of Harms "Trace-based
81 * task tree generation". The extended paper was submitted to the IntSys IARIA Journal.
82 * </p>
83 *
84 * @author Patrick Harms
85 */
86class CondenseSimilarTasksRule implements ISessionScopeRule {
87
88    /**
89     * <p>
90     * the task factory to be used for creating substructures for the temporal
91     * relationships identified during rule application
92     * </p>
93     */
94    private ITaskFactory taskFactory;
95    /**
96     * <p>
97     * the task builder to be used for creating substructures for the temporal relationships
98     * identified during rule application
99     * </p>
100     */
101    private ITaskBuilder taskBuilder;
102
103    /**
104     * <p>
105     * the task handling strategy to be used for comparing tasks during iteration detection an trie
106     * generation, i.e., after the tasks are harmonized
107     * </p>
108     */
109    private TaskHandlingStrategy identTaskHandlStrat;
110   
111    /**
112     * <p>
113     * instantiates the rule and initializes it with a task equality to be considered when
114     * comparing tasks as well as a task factory and builder to be used for creating task
115     * structures.
116     * </p>
117     *
118     * @param minimalTaskEquality the task equality to be considered when comparing tasks
119     * @param taskFactory         the task factory to be used for creating substructures
120     * @param taskBuilder         the task builder to be used for creating substructures
121     */
122    CondenseSimilarTasksRule(TaskEquality minimalTaskEquality,
123                             ITaskFactory taskFactory,
124                             ITaskBuilder taskBuilder)
125    {
126        this.taskFactory = taskFactory;
127        this.taskBuilder = taskBuilder;
128       
129        this.identTaskHandlStrat = new TaskHandlingStrategy(TaskEquality.IDENTICAL);
130    }
131
132    /* (non-Javadoc)
133     * @see java.lang.Object#toString()
134     */
135    @Override
136    public String toString() {
137        return "CondenseSimilarTasksRule";
138    }
139
140    /* (non-Javadoc)
141     * @see de.ugoe.cs.autoquest.tasktrees.temporalrelation.ISessionScopeRule#apply(java.util.List)
142     */
143    @Override
144    public RuleApplicationResult apply(List<IUserSession> sessions) {
145       
146        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
147        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
148        /*final List<IEventTaskInstance> formerInstances = new ArrayList<>();
149        final List<IEventTaskInstance> newInstances = new ArrayList<>();
150       
151        try {
152            ITaskInstanceVisitor visitor = new DefaultTaskInstanceTraversingVisitor() {
153                @Override
154                public void visit(IEventTaskInstance eventTaskInstance) {
155                    formerInstances.add(eventTaskInstance);
156                }
157            };
158           
159            PrintStream out = new PrintStream(new FileOutputStream(new File("01.out")));
160           
161            for (IUserSession session : sessions) {
162                new TaskTreeEncoder().encode(session, out, null);
163               
164                for (ITaskInstance instance : session) {
165                    instance.accept(visitor);
166                }
167            }
168            out.close();
169        }
170        catch (FileNotFoundException e) {
171            e.printStackTrace();
172        }
173       
174        PrintStream fileout = null;
175        try {
176            fileout = new PrintStream("sysout.txt");
177        }
178        catch (FileNotFoundException e1) {
179            e1.printStackTrace();
180        }
181       
182        PrintStream origOut = System.out;
183        if (fileout != null) {
184            System.setOut(fileout);
185        }*/
186       
187        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
188        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
189       
190       
191        RuleApplicationData appData = new RuleApplicationData(sessions);
192        //int mergecount = 0;
193       
194        do {
195            appData.setTaskModel(taskFactory.createTaskModel(sessions));
196           
197            /*mergecount++;
198            System.out.println(mergecount);
199            if ((mergecount % 20) == 0) {
200                System.out.println("performing validation " + mergecount);
201                new de.ugoe.cs.autoquest.tasktrees.temporalrelation.utils.TaskTreeValidator()
202                    .validate(appData.getTaskModel().getUserSessions(), true);
203            }*/
204           
205            appData.setMostSimilarTasks(null);
206           
207            // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
208            // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>
209            // for (ITask task : appData.getTaskModel().getTasks()) {
210            //     if (task.getInstances().size() <= 0) {
211            //         throw new RuntimeException("task " + task + " has no instances anymore");
212            //     }
213            //     
214            //     try {
215            //         new TaskTreeValidator().validate(task);
216            //     }
217            //     catch (Exception e) {
218            //         new TaskTreeEncoder().encode(task, System.err);
219            //     }
220            // }
221            //
222            // new TaskTreeValidator().validate(sessions);
223           
224            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
225            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<
226
227
228            Console.println("condensing " + appData.getTaskModel().getTasks().size() + " tasks");
229
230            getMostSimilarTasks(appData);
231
232            if (appData.getMostSimilarTasks() != null) {
233                for (SimilarTasks mostSimilarTask : appData.getMostSimilarTasks()) {
234                    handleSimilarTasks(mostSimilarTask, appData);
235                    appData.markAsAlreadyCondensed
236                        (mostSimilarTask.getLeftHandSide(), mostSimilarTask.getRightHandSide());
237                }
238            }
239           
240            harmonizeModel(sessions, appData.getTaskModel());
241        }
242        while (appData.getMostSimilarTasks() != null);
243       
244       
245        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
246        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
247        /*try {
248            ITaskInstanceVisitor visitor = new DefaultTaskInstanceTraversingVisitor() {
249                @Override
250                public void visit(IEventTaskInstance eventTaskInstance) {
251                    newInstances.add(eventTaskInstance);
252                }
253            };
254           
255            PrintStream out = new PrintStream(new FileOutputStream(new File("02.out")));
256           
257            for (IUserSession session : sessions) {
258                new TaskTreeEncoder().encode(session, out, null);
259               
260                for (ITaskInstance instance : session) {
261                    instance.accept(visitor);
262                }
263            }
264            out.close();
265        }
266        catch (FileNotFoundException e) {
267            e.printStackTrace();
268        }
269       
270        System.out.println("sizes  " + formerInstances.size() + "  " + newInstances.size());
271        for (int i = 0; i < newInstances.size(); i++) {
272            if (formerInstances.get(i) != newInstances.get(i)) {
273                System.out.println(i + "  " + formerInstances.get(i) + "  " + newInstances.get(i));
274            }
275        }
276       
277        System.setOut(origOut);
278        fileout.close();*/
279        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
280        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
281
282       
283        return appData.finalizeRuleApplicationResult();
284    }
285
286    /**
287     *
288     */
289    private void harmonizeModel(List<IUserSession> sessions, ITaskModel model) {
290        boolean somethingChanged;
291       
292        do {
293            // harmonize marking temporal relationships
294            MarkingTemporalRelationshipHarmonizer harmonizer =
295                new MarkingTemporalRelationshipHarmonizer();
296       
297            for (IUserSession session : sessions) {
298                for (ITaskInstance instance : session) {
299                    instance.accept(harmonizer);
300                }
301            }
302           
303            // integrate iterations performed once for children that were somewhere iterated more
304            // than once
305            SingleIterationExecutionsIntegrator integrator =
306                new SingleIterationExecutionsIntegrator(harmonizer.harmonizedIterations);
307
308            for (IUserSession session : sessions) {
309                for (ITaskInstance instance : session) {
310                    instance.accept(integrator);
311                }
312            }
313           
314            // search for sequences having subsequent identical children (may be the result
315            // of merging two subsequent children)
316            SubsequentIdenticalChildrenMerger merger = new SubsequentIdenticalChildrenMerger();
317            for (ITask task : model.getTasks()) {
318                merger.mergeSubsequentIdenticalTasks(task);
319            }
320           
321            for (IUserSession session : sessions) {
322                merger.mergeSubsequentIdenticalInstances(session);
323            }
324           
325            somethingChanged = harmonizer.somethingChanged || integrator.somethingChanged ||
326                merger.somethingChanged;
327                   
328        }
329        while(somethingChanged);
330    }
331
332    /**
333     *
334     */
335    private void getMostSimilarTasks(RuleApplicationData appData) {
336        // determine the list of interesting tasks
337        Collection<ITask> allTasks = appData.getTaskModel().getTasks();
338        Iterator<ITask> taskIterator = allTasks.iterator();
339        List<ITask> taskList = new ArrayList<ITask>(allTasks.size());
340       
341        while (taskIterator.hasNext()) {
342            ITask task = taskIterator.next();
343           
344            // only Sequences need to be compared with each other. Iterations differ only in their
345            // child, i.e., in the child sequences.
346            if (task instanceof ISequence) {
347                taskList.add(task);
348            }
349        }
350       
351        if (taskList.size() > 1) {
352            List<SimilarTasks> mostSimilarTasks =
353                appData.getMostSimilarTasksDeterminer().getMostSimilarTasks
354                    (taskList, appData.getTaskModel());
355           
356            if (mostSimilarTasks.size() > 0) {
357                appData.setMostSimilarTasks(mostSimilarTasks);
358            }
359        }
360    }
361
362    /**
363     *
364     */
365    private void handleSimilarTasks(SimilarTasks similarTasks, RuleApplicationData appData) {
366        // we need at least one instance per similar task. If not, it will not work and also
367        // does not make sense. No instances anymore can be caused by merging and hence
368        // discarding tasks in preceding merges.
369       
370        // similarTasks.dump(System.out);
371       
372        if ((similarTasks.getLeftHandSide().getInstances().size() <= 0) ||
373            (similarTasks.getRightHandSide().getInstances().size() <= 0))
374        {
375            /*System.out.println("a task exists that has no instances");
376            System.out.println(similarTasks.getLeftHandSide() + "  " +
377                               similarTasks.getLeftHandSide().getInstances().size());
378            System.out.println(similarTasks.getRightHandSide() + "  " +
379                               similarTasks.getRightHandSide().getInstances().size());*/
380           
381            throw new RuntimeException
382                ("one and the same task seems to belong to several similar tasks");
383        }
384       
385        similarTasks = SimilarTasks.getMergableLevelOfSimilarity
386            (similarTasks, identTaskHandlStrat.getTaskComparator());
387       
388        if ((similarTasks == null) || (similarTasks.getDiffLevel() == 100)) {
389            // this may happen, if no mergable level of similarity can be found
390            return;
391        }
392       
393        // similarTasks.dump(System.out);
394
395        List<FlattenInstruction> flattenInstructions =
396            getFlattenInstructions(similarTasks, appData);
397       
398        if (flattenInstructions == null) {
399            // we cannot merge this
400            return;
401        }
402       
403        // for (FlattenInstruction instruction : flattenInstructions) {
404        //     instruction.dump(System.out);
405        // }
406       
407        int noOfFlattenedInstances = similarTasks.getLeftHandSide().getInstances().size() +
408            similarTasks.getRightHandSide().getInstances().size();
409       
410        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
411        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
412        List<TaskPath> excludes = new ArrayList<TaskPath>();
413       
414        for (FlattenInstruction instruction : flattenInstructions) {
415            //System.out.println("exclude " + instruction.path);
416            excludes.add(instruction.path);
417        }
418        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
419        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
420
421        Map<ITaskInstance, IUserSession> flattenedSessions =
422            new HashMap<ITaskInstance, IUserSession>(noOfFlattenedInstances);
423        flattenInstances
424            (similarTasks.getLeftHandSide(), flattenInstructions, flattenedSessions, excludes);
425        flattenInstances
426            (similarTasks.getRightHandSide(), flattenInstructions, flattenedSessions, excludes);
427
428        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
429        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
430        /*for (IUserSession session : flattenedSessions.values()) {
431            System.out.println("user session {");
432           
433            for (ITaskInstance instance : session) {
434                System.out.println(instance);
435            }
436           
437            System.out.println("}");
438        }*/
439        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
440        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
441       
442        List<IUserSession> flattenedSessionList =
443            new LinkedList<IUserSession>(flattenedSessions.values());
444       
445        new SequenceForTaskDetectionRule
446            (TaskEquality.IDENTICAL, taskFactory, taskBuilder, 0).apply(flattenedSessionList);
447       
448        Map<ITaskInstance, ITaskInstance> replacements = new HashMap<ITaskInstance, ITaskInstance>();
449        ITask replacementTask = null;
450       
451        for (Map.Entry<ITaskInstance, IUserSession> entry : flattenedSessions.entrySet()) {
452            // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
453            // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
454            // the user sessions were sufficiently equal to have now only one common task as child
455           
456            if (entry.getValue().size() != 1) {
457                //new TaskTreeEncoder().encode(entry.getValue(), System.out, excludes);
458                throw new RuntimeException("flattened sessions were not combined as expected");
459            }
460           
461            if (replacementTask == null) {
462                replacementTask = entry.getValue().get(0).getTask();
463            }
464            else if (replacementTask != entry.getValue().get(0).getTask()) {
465                throw new RuntimeException("two separate replacement tasks were calculated as " +
466                                           "replacement, one for both tasks to be merged");
467            }
468           
469            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
470            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
471           
472            replacements.put(entry.getKey(), entry.getValue().get(0));
473        }
474       
475        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
476        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
477        // ((Task) replacementTask).setDescription
478        //     (replacementTask + " calculated as full replacement for " +
479        //      similarTasks.getLeftHandSide() + " and " + similarTasks.getRightHandSide());
480   
481        int allInstances = similarTasks.getLeftHandSide().getInstances().size() +
482            similarTasks.getRightHandSide().getInstances().size();
483       
484        if (replacements.size() != allInstances) {
485            throw new RuntimeException("number of replacements does not match number of instances");
486        }
487       
488        /*if (replacementTask != null) {
489            System.out.println("replacement task is calculated to be: ");
490            new de.ugoe.cs.autoquest.tasktrees.temporalrelation.utils.TaskTreeEncoder().encode
491                (replacementTask, System.out);
492        }*/
493       
494        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
495        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
496
497        ITask uniqueReplacement = appData.ensureUnique(replacementTask);
498       
499        if (uniqueReplacement != replacementTask) {
500            System.out.println("updating task instances with a unified replacement task");
501            for (ITaskInstance replacement : replacements.values()) {
502                taskBuilder.setTask(replacement, uniqueReplacement);
503            }
504        }
505
506        for (IUserSession session : appData.getSessions()) {
507            replaceTaskInstances(session, replacements, similarTasks);
508        }
509       
510       
511        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
512        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
513        if (replacements.size() != 0) {
514            for (Map.Entry<ITaskInstance, ITaskInstance> entry : replacements.entrySet()) {
515                System.out.println
516                    ("did not replace instance " + entry.getKey() + " with " + entry.getValue());
517            }
518           
519            throw new RuntimeException();
520        }
521        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
522        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
523    }
524
525    /**
526     *
527     */
528    private List<FlattenInstruction> getFlattenInstructions(SimilarTasks        similarTasks,
529                                                            RuleApplicationData appData)
530    {
531        TaskTraversal leftHandSideTraversal = similarTasks.getLeftHandSideTraversal();
532        TaskTraversal rightHandSideTraversal = similarTasks.getRightHandSideTraversal();
533       
534        List<FlattenInstruction> result = new LinkedList<FlattenInstruction>();
535        TaskComparator comp = identTaskHandlStrat.getTaskComparator();
536       
537        // first create instructions for the deltas
538        for (Delta delta : similarTasks.getPatch().getDeltas()) {
539            if ((delta.getType() == Delta.TYPE.INSERT) ||
540                (delta.getType() == Delta.TYPE.DELETE))
541            {
542                // System.out.println("handling " + delta.getType());
543                Chunk chunk;
544                TaskPath insertAfterPath = null;
545                TaskPath insertBeforePath;
546               
547                if (delta.getType() == Delta.TYPE.INSERT) {
548                    chunk = delta.getRevised();
549                    int pos = delta.getOriginal().getPosition();
550                    // System.out.println(" position " + pos);
551                   
552                    if (pos > 0) {
553                        insertAfterPath = leftHandSideTraversal.getTraversalPaths()[pos - 1];
554                    }
555                    insertBeforePath = leftHandSideTraversal.getTraversalPaths()[pos];
556                }
557                else {
558                    chunk = delta.getOriginal();
559                    int pos = delta.getRevised().getPosition();
560                    if (pos > 0) {
561                        insertAfterPath = rightHandSideTraversal.getTraversalPaths()[pos - 1];
562                    }
563                    insertBeforePath = rightHandSideTraversal.getTraversalPaths()[pos];
564                }
565               
566                ITask child = getTaskForChunk(chunk, appData);
567                IOptional optional;
568               
569                if (child instanceof IOptional) {
570                    optional = (IOptional) child;
571                }
572                else {
573                    optional = taskFactory.createNewOptional();
574                    taskBuilder.setMarkedTask(optional, child);
575                    // ((Task) optional).setDescription(optional.getDescription() +
576                    //                                  " created for " + delta.getType());
577                    optional = (IOptional) appData.ensureUnique(optional);
578                    createReplacementInstructions(chunk, optional, null, result);
579                }
580
581                // create a flatten instruction for the non-occurrence of the optional
582                result.add(new FlattenInstruction(insertAfterPath, insertBeforePath, optional));
583               
584            }
585            else if (delta.getType() == Delta.TYPE.CHANGE) {
586                ITask child1;
587                ITask child2;
588               
589                // check, if the change covers the whole traversal. If so reuse the root task.
590                // Otherwise, create intermediate tasks if required representing both sides
591                // of changes
592                if ((delta.getOriginal().getPosition() == 0) &&
593                    (delta.getOriginal().size() == similarTasks.getLeftHandSideTraversal().size()))
594                {
595                    child1 = similarTasks.getLeftHandSide();
596                }
597                else {
598                    child1 = getTaskForChunk(delta.getOriginal(), appData);
599                }
600               
601                if ((delta.getRevised().getPosition() == 0) &&
602                    (delta.getRevised().size() == similarTasks.getRightHandSideTraversal().size()))
603                {
604                    child2 = similarTasks.getRightHandSide();
605                }
606                else {
607                    child2 = getTaskForChunk(delta.getRevised(), appData);
608                }
609               
610                // check if either of the variants is an iteration or optional of the respective
611                // other variant. If so, ensure, that the iteration or optional are preserved
612                ITask markedTask1 = (child1 instanceof IMarkingTemporalRelationship) ?
613                    ((IMarkingTemporalRelationship) child1).getMarkedTask() : null;
614               
615                ITask markedTask2 = (child2 instanceof IMarkingTemporalRelationship) ?
616                    ((IMarkingTemporalRelationship) child2).getMarkedTask() : null;
617               
618                if (comp.equals(markedTask1, child2)) {
619                    if (child1 instanceof IOptional) {
620                        createReplacementInstructions
621                            (delta.getRevised(), (IOptional) child1, null, result);
622                    }
623                    else {
624                        throw new java.lang.UnsupportedOperationException("not implemented yet");
625                    }
626                }
627                else if (comp.equals(markedTask2, child1)) {
628                    if (child2 instanceof IOptional) {
629                        createReplacementInstructions
630                            (delta.getOriginal(), (IOptional) child2, null, result);
631                    }
632                    else {
633                        throw new java.lang.UnsupportedOperationException("not implemented yet");
634                    }
635                }
636                else if ((markedTask1 != null) && (comp.equals(markedTask1, markedTask2))) {
637                    throw new java.lang.UnsupportedOperationException("not implemented yet");
638                }
639                else {
640                    // its time to create a selection of both variants. If one is already
641                    // a selection, it is reused and extended, if required.
642                   
643                    ITask expectedChild1 = null;
644                    ITask expectedChild2 = null;
645                   
646                    ISelection selection = null;
647                    if (child1 instanceof ISelection) {
648                        selection = (ISelection) child1;
649                    }
650               
651                    if (child2 instanceof ISelection) {
652                        if (selection == null) {
653                            // only child 2 is a selection --> extend it with child 1
654                            selection = (ISelection) child2;
655                            addSelectionChildIfRequired(selection, child1);
656                            expectedChild1 = child1;
657                        }
658                        else {
659                            // both are selections --> extend child 1 with variants of child2
660                            for (ITask child : ((ISelection) child2).getChildren()) {
661                                addSelectionChildIfRequired(selection, child);
662                            }
663                        }
664                    }
665                    else if (selection != null) {
666                        // only child 1 is a selection --> extend it with child 2
667                        addSelectionChildIfRequired(selection, child2);
668                        expectedChild2 = child2;
669                    }
670                    else if (selection == null) {
671                        // it may also be the case, that one is a child of a selection and occurs
672                        // only as such and nowhere else. Then the parent selection is reused and
673                        // extended.
674                        for (ITask candidate : appData.taskModel.getTasks()) {
675                            if (candidate instanceof ISelection) {
676                                selection = (ISelection) candidate;
677                                ITask existingChildTask = null;
678                               
679                                if (selection.getChildren().contains(child1)) {
680                                    existingChildTask = child1;
681                                }
682                                else if (selection.getChildren().contains(child2)) {
683                                    existingChildTask = child2;
684                                }
685                               
686                                if (existingChildTask != null) {
687                                    int noOfInstances = 0;
688                                   
689                                    for (ITaskInstance instance : selection.getInstances()) {
690                                        ITaskInstance childInstance =
691                                            ((ISelectionInstance) instance).getChild();
692                                           
693                                        if (childInstance.getTask() == existingChildTask) {
694                                            noOfInstances++;
695                                        }
696                                    }
697                                   
698                                    if (noOfInstances < existingChildTask.getInstances().size()) {
699                                        // not all instances of the existing child task are covered
700                                        // by the parent selection. Hence, throw selection away and
701                                        // search for another one
702                                        selection = null;
703                                    }
704                                    else {
705                                        // found a parent selection --> reuse it but add the new
706                                        // variant
707                                        if (child1 == existingChildTask) {
708                                            addSelectionChildIfRequired(selection, child2);
709                                        }
710                                        else {
711                                            addSelectionChildIfRequired(selection, child1);
712                                        }
713                                        expectedChild1 = child1;
714                                        expectedChild2 = child2;
715                                        break;
716                                    }
717                                }
718                                else {
719                                    selection = null;
720                                }
721                            }
722                        }
723                       
724                        if (selection == null) {
725                            // none of both is already a selection, so create a new one with both
726                            // of the children as variants
727                            selection = taskFactory.createNewSelection();
728                            // ((Task) selection).setDescription(selection.getDescription() +
729                            //                                   " created for " + delta.getType());
730                            taskBuilder.addChild(selection, child1);
731                            taskBuilder.addChild(selection, child2);
732                            expectedChild1 = child1;
733                            expectedChild2 = child2;
734                        }
735                    }
736                   
737                    // tasks that refer to themselves are not valid. But due to the creation of
738                    // selections, this may happen. This must be prevented
739                    /*final Set<ITask> tasks = new HashSet<ITask>();
740                   
741                    try {
742                        selection.accept(new DefaultTaskTraversingVisitor() {
743                            @Override
744                            public void visit(ITask task) {
745                                if (!tasks.contains(task)) {
746                                    tasks.add(task);
747                                    super.visit(task);
748                                }
749                                else {
750                                    throw new RuntimeException("tasks refers to itself");
751                                }
752                            }
753                        });
754                    }
755                    catch (RuntimeException e) {
756                        // a task refers to itself
757                        return null;
758                    }*/
759                   
760                    // the new selection does not refer to itself, no apply it
761                    selection = (ISelection) appData.ensureUnique(selection);
762                   
763                    createReplacementInstructions
764                        (delta.getOriginal(), selection, expectedChild1, result);
765                    createReplacementInstructions
766                        (delta.getRevised(), selection, expectedChild2, result);
767                }
768            }
769        }
770       
771        // create instructions to harmonize marking temporal relationships for untraversed tasks
772        int leftHandSideIndex = 0;
773        int rightHandSideIndex = 0;
774       
775        OUTER:
776        while (leftHandSideIndex < leftHandSideTraversal.getTraversalPaths().length) {
777            for (Delta delta : similarTasks.getPatch().getDeltas()) {
778                if (delta.getOriginal().getPosition() == leftHandSideIndex) {
779                    if (delta.getType() == Delta.TYPE.INSERT) {
780                        rightHandSideIndex += delta.getRevised().size();
781                        // do not continue OUTER to ensure, that the left hand side and the
782                        // right hand side coming directly after the insert are handled
783                    }
784                    else if (delta.getType() == Delta.TYPE.DELETE) {
785                        leftHandSideIndex += delta.getOriginal().size();
786                        continue OUTER;
787                    }
788                    else if (delta.getType() == Delta.TYPE.CHANGE) {
789                        leftHandSideIndex += delta.getOriginal().size();
790                        rightHandSideIndex += delta.getRevised().size();
791                        continue OUTER;
792                    }
793                }
794            }
795           
796            TaskPath leftPath = leftHandSideTraversal.getTraversalPaths()[leftHandSideIndex];
797            TaskPath rightPath = rightHandSideTraversal.getTraversalPaths()[rightHandSideIndex];
798           
799            if (comp.equals(leftPath.getLast(), rightPath.getLast())) {
800                if (leftPath.getTask(leftPath.size() - 2) instanceof IOptional) {
801                    IOptional optional = (IOptional) leftPath.getTask(leftPath.size() - 2);
802                    result.add
803                        (new FlattenInstruction(leftPath.subPath(0, leftPath.size()), optional));
804
805                    result.add(new FlattenInstruction(rightPath, optional));
806                }
807                else if (rightPath.getTask(rightPath.size() - 2) instanceof IOptional) {
808                    IOptional optional = (IOptional) rightPath.getTask(rightPath.size() - 2);
809                    result.add
810                        (new FlattenInstruction(rightPath.subPath(0, rightPath.size()), optional));
811
812                    result.add(new FlattenInstruction(leftPath, optional));
813                }
814            }
815           
816            leftHandSideIndex++;
817            rightHandSideIndex++;
818        }
819       
820       
821        // then create instructions for what stays the same
822        OUTER:
823        for (TaskPath path : leftHandSideTraversal.getTraversalPaths()) {
824            for (FlattenInstruction instruction : result) {
825                if ((instruction.getInstruction() != FlattenInstruction.Instruction.INTEGRATE_OPTIONAL) &&
826                    (instruction.matches(path)))
827                {
828                    continue OUTER;
829                }
830            }
831           
832            result.add(new FlattenInstruction(path));
833        }
834
835        OUTER:
836        for (TaskPath path : rightHandSideTraversal.getTraversalPaths()) {
837            for (FlattenInstruction instruction : result) {
838                if ((instruction.getInstruction() != FlattenInstruction.Instruction.INTEGRATE_OPTIONAL) &&
839                    (instruction.matches(path)))
840                {
841                    continue OUTER;
842                }
843            }
844           
845            result.add(new FlattenInstruction(path));
846        }
847
848        return result;
849    }
850
851    /**
852     *
853     */
854    private void addSelectionChildIfRequired(ISelection selection, ITask child) {
855        if (TaskTreeUtils.isChild(selection, child)) {
856            System.out.println(selection);
857            System.out.println(child);
858            throw new RuntimeException("child of selection has selection itself as child");
859        }
860        for (ITask candidate : selection.getChildren()) {
861            if (identTaskHandlStrat.getTaskComparator().equals(candidate, child)) {
862                return;
863            }
864        }
865       
866        taskBuilder.addChild(selection, child);
867    }
868
869    /**
870     *
871     */
872    private ITask getTaskForChunk(Chunk chunk, RuleApplicationData appData) {
873        if (chunk.size() == 1) {
874            TaskPath path = (TaskPath) chunk.getLines().get(0);
875            return path.getTask(path.size() - 1);
876        }
877        else {
878            ISequence task = taskFactory.createNewSequence();
879            // ((Task) task).setDescription(task.getDescription() +
880            //                              " created to represent a chunk");
881
882            for (Object pathObj : chunk.getLines()) {
883                TaskPath path = (TaskPath) pathObj;
884                taskBuilder.addChild(task, path.getLast());
885            }
886           
887            return appData.ensureUnique(task);
888        }
889    }
890
891    /**
892     *
893     */
894    private void createReplacementInstructions(Chunk                    chunk,
895                                               ITask                    replacement,
896                                               ITask                    selectedChild,
897                                               List<FlattenInstruction> result)
898    {
899        // create a flatten instruction for the occurrence of the replacement
900        for (Object pathObj : chunk.getLines()) {
901            TaskPath path = (TaskPath) pathObj;
902           
903            if (replacement instanceof IOptional) {
904                result.add(new FlattenInstruction(path, (IOptional) replacement));
905            }
906            else if (replacement instanceof ISelection) {
907                result.add
908                    (new FlattenInstruction(path, (ISelection) replacement, selectedChild));
909            }
910        }
911    }
912
913    /**
914     *
915     */
916    private void flattenInstances(ITask                            task,
917                                  List<FlattenInstruction>         flattenInstructions,
918                                  Map<ITaskInstance, IUserSession> flattenedSessions,
919                                  List<TaskPath>                   excludes)
920    {
921        //int i = 0;
922        for (ITaskInstance instance : task.getInstances()) {
923            /*System.out.println("flattening instance " + i++ + " of task " + task);
924            new TaskTreeEncoder().encode(instance, System.out, excludes);*/
925           
926            TaskPath taskPath = new TaskPath();
927            taskPath.add(instance.getTask(), 0);
928            IUserSession session = taskFactory.createUserSession();
929            flattenInstance(instance, taskPath, flattenInstructions, session,
930                            new LinkedList<TaskPath>());
931           
932            //new TaskTreeEncoder().encode(session, System.out, excludes);
933           
934            flattenedSessions.put(instance, session);
935        }
936    }
937
938    /**
939     *
940     */
941    private void flattenInstance(ITaskInstance            instance,
942                                 TaskPath                 taskPath,
943                                 List<FlattenInstruction> flattenInstructions,
944                                 IUserSession             session,
945                                 List<TaskPath>           previousPaths)
946    {
947       
948        TaskComparator comp = identTaskHandlStrat.getTaskComparator();
949       
950        // System.out.println("applying integrate optional instructions on " + taskPath);
951       
952        for (FlattenInstruction instruction : flattenInstructions) {
953            if ((instruction.getInstruction() == FlattenInstruction.Instruction.INTEGRATE_OPTIONAL) &&
954                (instruction.matches(taskPath)))
955            {
956                // System.out.print("found instruction ");
957                // instruction.dump(System.out);
958                TaskPath previousPath = previousPaths.size() > 0 ?
959                    previousPaths.get(previousPaths.size() - 1) : null;
960               
961                if (pathsMatch(instruction.getPrecedingPath(), previousPath)) {
962                    IOptional optional = instruction.getOptional();
963                    IOptionalInstance optionalInstance =
964                            taskFactory.createNewTaskInstance(optional);
965                    taskBuilder.addTaskInstance(session, optionalInstance);
966                }
967               
968                break;
969            }
970        }
971       
972        boolean instanceHandled = false;
973        // System.out.println("applying other instructions on " + taskPath);
974       
975        for (FlattenInstruction instruction : flattenInstructions) {
976            if (instruction.matches(taskPath)) {
977                // System.out.print("found instruction ");
978                // instruction.dump(System.out);
979               
980                switch (instruction.getInstruction()) {
981                    case DONT_TRAVERSE: {
982                        if (instance != null) {
983                            taskBuilder.addTaskInstance(session, instance);
984                        }
985                        instanceHandled = true;
986                        break;
987                    }
988                    case MAKE_OPTIONAL: {
989                        instanceHandled = true;
990                       
991                        if (instance == null) {
992                            break;
993                        }
994                       
995                        IOptional optional = instruction.getOptional();
996
997                        if (comp.equals(optional, instance.getTask())) {
998                            // the instance is already an instance of the correct optional --> reuse
999                            taskBuilder.addTaskInstance(session, instance);
1000                        }
1001                        else if (comp.equals(optional.getMarkedTask(), instance.getTask())) {
1002                            IOptionalInstance optionalInstance =
1003                                taskFactory.createNewTaskInstance(optional);
1004                            taskBuilder.setChild(optionalInstance, instance);
1005                            taskBuilder.addTaskInstance(session, optionalInstance);
1006                        }
1007                        else if (optional.getMarkedTask() instanceof ISequence) {
1008                            // first check, if the instance was already created, if not create it
1009                            ISequenceInstance sequenceInstance = ensureSequenceChildInstanceFor
1010                                (optional, (ISequence) optional.getMarkedTask(), session);
1011
1012                            taskBuilder.addChild(sequenceInstance, instance);
1013                        }
1014                        break;
1015                    }
1016                    case MAKE_SELECTION: {
1017                        instanceHandled = true;
1018
1019                        if (instance == null) {
1020                            break;
1021                        }
1022                       
1023                        ISelection selection = instruction.getSelection();
1024
1025                        if (comp.equals(instruction.getSelectedChild(), instance.getTask())) {
1026                            ISelectionInstance selectionInstance =
1027                                taskFactory.createNewTaskInstance(selection);
1028                            taskBuilder.setChild(selectionInstance, instance);
1029                            taskBuilder.addTaskInstance(session, selectionInstance);
1030                        }
1031                        else if (instruction.getSelectedChild() == null) {
1032                            // both variants were already selections. They will have been merged.
1033                            // Hence we can reuse the child instances of the existing selection
1034                            // instances.
1035                            if (comp.equals(instance.getTask(), selection)) {
1036                                taskBuilder.addTaskInstance(session, instance);
1037                            }
1038                            else {
1039                                ISelectionInstance selectionInstance =
1040                                    taskFactory.createNewTaskInstance(selection);
1041                                taskBuilder.setChild(selectionInstance,
1042                                                     ((ISelectionInstance) instance).getChild());
1043                                taskBuilder.addTaskInstance(session, selectionInstance);
1044                               
1045                                taskBuilder.discardTaskInstance(instance);
1046                            }
1047                        }
1048                        else if (instruction.getSelectedChild() instanceof ISequence) {
1049                            // first check, if the instance was already created, if not create it
1050                            ISequenceInstance sequenceInstance = ensureSequenceChildInstanceFor
1051                                (selection, (ISequence) instruction.getSelectedChild(), session);
1052
1053                            taskBuilder.addChild(sequenceInstance, instance);
1054                        }
1055
1056                        break;
1057                    }
1058                    case INTEGRATE_OPTIONAL: {
1059                        // have been applied beforehand
1060                        break;
1061                    }
1062                    default : {
1063                        throw new RuntimeException("unknown instruction type");
1064                    }
1065                }
1066            }
1067           
1068            if (instanceHandled) {
1069                break;
1070            }
1071        }
1072       
1073        if (!instanceHandled) {
1074            ITask task = taskPath.getLast();
1075            if (task instanceof IIteration) {
1076                taskPath.add(((IIteration) task).getMarkedTask(), 0);
1077               
1078                if (instance != null) {
1079                    for (ITaskInstance child : (IIterationInstance) instance) {
1080                        flattenInstance
1081                            (child, taskPath, flattenInstructions, session, previousPaths);
1082                    }
1083                }
1084                else {
1085                    flattenInstance(null, taskPath, flattenInstructions, session, previousPaths);
1086                }
1087               
1088                taskPath.removeLast();
1089            }
1090            else if (task instanceof ISequence) {
1091                List<ITask> children = ((ISequence) task).getChildren();
1092                for (int i = 0; i < children.size(); i++) {
1093                    ITaskInstance child = null;
1094                    if (instance != null) {
1095                        child = ((ISequenceInstance) instance).get(i);
1096                    }
1097                    taskPath.add(children.get(i), i);
1098                    flattenInstance
1099                        (child, taskPath, flattenInstructions, session, previousPaths);
1100                    taskPath.removeLast();
1101                }
1102            }
1103            else if (task instanceof ISelection) {
1104                if (instance != null) {
1105                    taskPath.add(((ISelectionInstance) instance).getChild().getTask(), 0);
1106                    flattenInstance(((ISelectionInstance) instance).getChild(), taskPath,
1107                                    flattenInstructions, session, previousPaths);
1108                    taskPath.removeLast();
1109                }
1110                else {
1111                    // check if the selection has any child for which a rule must be considered
1112                    List<ITask> children = ((ISelection) task).getChildren();
1113                    for (int i = 0; i < children.size(); i++) {
1114                        taskPath.add(children.get(i), i);
1115                        flattenInstance
1116                            (null, taskPath, flattenInstructions, session, previousPaths);
1117                        taskPath.removeLast();
1118                    }
1119                }
1120            }
1121            else if (task instanceof IOptional) {
1122                taskPath.add(((IOptional) task).getMarkedTask(), 0);
1123                ITaskInstance child = null;
1124                if (instance != null) {
1125                    child = ((IOptionalInstance) instance).getChild();
1126                }
1127                flattenInstance(child, taskPath, flattenInstructions, session, previousPaths);
1128                taskPath.removeLast();
1129               
1130                if ((instance != null) && (child == null)) {
1131                    // add the empty optional instance
1132                    taskBuilder.addTaskInstance(session, instance);
1133                    previousPaths.add(new TaskPath(taskPath));
1134                }
1135            }
1136            else if (instance != null) {
1137                taskBuilder.addTaskInstance(session, instance);
1138                previousPaths.add(new TaskPath(taskPath));
1139            }
1140        }
1141        else {
1142            previousPaths.add(new TaskPath(taskPath));
1143        }
1144    }
1145
1146    /**
1147     *
1148     */
1149    private ISequenceInstance ensureSequenceChildInstanceFor(ITask        replacement,
1150                                                             ISequence    expectedChildSequence,
1151                                                             IUserSession session)
1152    {
1153        // first check, if there is already an instance of the expected sequence. If so, check if
1154        // its child is a sequence instance. If not create a new task instance with an appropriate
1155        // sequence instance
1156        ITaskInstance prevInstance =
1157            session.size() > 0 ? session.get(session.size() - 1) : null;
1158       
1159        ISequenceInstance sequenceInstance = null;
1160       
1161        if ((prevInstance != null) &&
1162            (identTaskHandlStrat.getTaskComparator().equals(prevInstance.getTask(), replacement)))
1163        {
1164            if ((prevInstance instanceof IOptionalInstance) &&
1165                (((IOptionalInstance) prevInstance).getChild() instanceof ISequenceInstance))
1166            {
1167                sequenceInstance =
1168                    (ISequenceInstance) ((IOptionalInstance) prevInstance).getChild();
1169            }
1170            else if ((prevInstance instanceof ISelectionInstance) &&
1171                    (((ISelectionInstance) prevInstance).getChild() instanceof ISequenceInstance))
1172            {
1173                sequenceInstance =
1174                    (ISequenceInstance) ((ISelectionInstance) prevInstance).getChild();
1175            }
1176        }
1177       
1178        // although we found a sequence instance as expected, this instance might already be fully
1179        // created and a new (iterated) one must be created. Check for it and handle correctly
1180        if ((sequenceInstance != null) &&
1181            (sequenceInstance.size() == expectedChildSequence.getChildren().size()))
1182        {
1183            sequenceInstance = null;
1184        }
1185       
1186        if (sequenceInstance == null) {
1187            //System.out.println("creating new sequence instance of selected child");
1188            sequenceInstance = taskFactory.createNewTaskInstance(expectedChildSequence);
1189               
1190            ITaskInstance replacementInstance = null;
1191           
1192            if (replacement instanceof IOptional) {
1193                replacementInstance = taskFactory.createNewTaskInstance((IOptional) replacement);
1194                taskBuilder.setChild((IOptionalInstance) replacementInstance, sequenceInstance);
1195            }
1196            else if (replacement instanceof ISelection) {
1197               
1198                /*System.out.println("replacement: ");
1199                new TaskTreeEncoder().encode(replacement, System.out);
1200               
1201                System.out.println("expectedChild: ");
1202                new TaskTreeEncoder().encode(expectedChildSequence, System.out);*/
1203               
1204                replacementInstance = taskFactory.createNewTaskInstance((ISelection) replacement);
1205                taskBuilder.setChild((ISelectionInstance) replacementInstance, sequenceInstance);
1206            }
1207           
1208            taskBuilder.addTaskInstance(session, replacementInstance);
1209        }
1210       
1211        return sequenceInstance;
1212    }
1213
1214    /**
1215     *
1216     */
1217    private void replaceTaskInstances(ITaskInstanceList                 taskInstanceList,
1218                                      Map<ITaskInstance, ITaskInstance> replacements,
1219                                      SimilarTasks                      similarTasks)
1220    {
1221        for (int i = 0; i < taskInstanceList.size(); i++) {
1222            ITaskInstance childInstance = taskInstanceList.get(i);
1223            ITaskInstance replacement = replacements.remove(childInstance);
1224           
1225            if (replacement != null) {
1226               
1227                // update the model for sequences (others are updated in the calling method)
1228                if (taskInstanceList instanceof ISequenceInstance) {
1229                    ISequence task = ((ISequenceInstance) taskInstanceList).getSequence();
1230                    taskBuilder.setChild(task, i, replacement.getTask());
1231                }
1232                else if (taskInstanceList instanceof IIterationInstance) {
1233                    IIteration task = ((IIterationInstance) taskInstanceList).getIteration();
1234                    taskBuilder.setMarkedTask(task, replacement.getTask());
1235                }
1236               
1237                // perform the actual replacement and throw away the instance
1238                taskBuilder.setTaskInstance(taskInstanceList, i, replacement);
1239                TaskPath path = new TaskPath();
1240                path.add(childInstance.getTask(), 0);
1241                discardTaskInstancesNotBelongingToTraversals(childInstance, path, similarTasks);
1242            }
1243            else {
1244                replaceTaskInstances(childInstance, replacements, similarTasks);
1245            }
1246        }
1247    }
1248
1249    /**
1250     *
1251     */
1252    private void replaceTaskInstances(IOptionalInstance                 optionalInstance,
1253                                      Map<ITaskInstance, ITaskInstance> replacements,
1254                                      SimilarTasks                      similarTasks)
1255    {
1256        ITaskInstance childInstance = optionalInstance.getChild();
1257       
1258        if (childInstance != null) {
1259            ITaskInstance replacement = replacements.remove(childInstance);
1260           
1261            if (replacement != null) {
1262                // do not update the model --> is updated in the calling method
1263               
1264                // perform the actual replacement and throw away the instance
1265                taskBuilder.setMarkedTask(optionalInstance.getOptional(), replacement.getTask());
1266                taskBuilder.setChild(optionalInstance, replacement);
1267                TaskPath path = new TaskPath();
1268                path.add(childInstance.getTask(), 0);
1269                discardTaskInstancesNotBelongingToTraversals(childInstance, path, similarTasks);
1270            }
1271            else {
1272                replaceTaskInstances(childInstance, replacements, similarTasks);
1273            }
1274        }
1275    }
1276
1277    /**
1278     *
1279     */
1280    private void replaceTaskInstances(ISelectionInstance                selectionInstance,
1281                                      Map<ITaskInstance, ITaskInstance> replacements,
1282                                      SimilarTasks                      similarTasks)
1283    {
1284        TaskComparator comparator = identTaskHandlStrat.getTaskComparator();
1285       
1286        ITaskInstance childInstance = selectionInstance.getChild();
1287       
1288        if (childInstance != null) {
1289            ITaskInstance replacement = replacements.remove(childInstance);
1290           
1291            if (replacement != null) {
1292               
1293                if (replacement instanceof ISelectionInstance) {
1294                    // if the replacement itself is a selection instance, we cannot add
1295                    // a selection instance as the child of the parent selection instance.
1296                    // therefore, we just use the child of the replacement as new child of the
1297                    // existing selection instance
1298                    taskBuilder.discardTaskInstance(replacement);
1299                    replacement = ((ISelectionInstance) replacement).getChild();
1300                }
1301               
1302                // update the model
1303                // we replace all instances of the merged tasks with instances of a new task.
1304                // hence, we also have to remove the task of the replaced children from the
1305                // available variants of the selection
1306                taskBuilder.removeChild(selectionInstance.getSelection(), childInstance.getTask());
1307
1308                boolean found = false;
1309                for (ITask child : selectionInstance.getSelection().getChildren()) {
1310                    if (comparator.equals(child, replacement.getTask())) {
1311                        found = true;
1312                        break;
1313                    }
1314                }
1315               
1316                if (!found) {
1317                    taskBuilder.addChild(selectionInstance.getSelection(), replacement.getTask());
1318                }
1319               
1320                // perform the actual replacement and throw away the instance
1321                taskBuilder.setChild(selectionInstance, replacement);
1322                TaskPath path = new TaskPath();
1323                path.add(childInstance.getTask(), 0);
1324                discardTaskInstancesNotBelongingToTraversals(childInstance, path, similarTasks);
1325            }
1326            else {
1327                replaceTaskInstances(childInstance, replacements, similarTasks);
1328            }
1329        }
1330    }
1331
1332    /**
1333     *
1334     */
1335    private void replaceTaskInstances(ITaskInstance                     childInstance,
1336                                      Map<ITaskInstance, ITaskInstance> replacements,
1337                                      SimilarTasks                      similarTasks)
1338    {
1339        if (childInstance instanceof IIterationInstance) {
1340            replaceTaskInstances((ITaskInstanceList) childInstance, replacements, similarTasks);
1341        }
1342        else if (childInstance instanceof IOptionalInstance) {
1343            replaceTaskInstances((IOptionalInstance) childInstance, replacements, similarTasks);
1344        }
1345        else if (childInstance instanceof ISelectionInstance) {
1346            replaceTaskInstances((ISelectionInstance) childInstance, replacements, similarTasks);
1347        }
1348        else if (childInstance instanceof ISequenceInstance) {
1349            replaceTaskInstances((ITaskInstanceList) childInstance, replacements, similarTasks);
1350        }
1351    }
1352
1353    /**
1354     *
1355     */
1356    private void discardTaskInstancesNotBelongingToTraversals(ITaskInstance instance,
1357                                                              TaskPath      pathToInstance,
1358                                                              SimilarTasks  similarTasks)
1359    {
1360        boolean discarded = false;
1361        for (TaskPath candidate : similarTasks.getLeftHandSideTraversal().getTraversalPaths()) {
1362            if (candidate.size() > pathToInstance.size()) {
1363                TaskPath potentialEqualSubPath = candidate.subPath(0, pathToInstance.size());
1364                if (pathsMatch(potentialEqualSubPath, pathToInstance)) {
1365                    taskBuilder.discardTaskInstance(instance);
1366                    discarded = true;
1367                    break;
1368                }
1369            }
1370        }
1371       
1372        if (!discarded) {
1373            for (TaskPath candidate : similarTasks.getRightHandSideTraversal().getTraversalPaths()) {
1374                if (candidate.size() > pathToInstance.size()) {
1375                    TaskPath potentialEqualSubPath = candidate.subPath(0, pathToInstance.size());
1376                    if (pathsMatch(potentialEqualSubPath, pathToInstance)) {
1377                        taskBuilder.discardTaskInstance(instance);
1378                        discarded = true;
1379                        break;
1380                    }
1381                }
1382            }
1383        }
1384       
1385        if (discarded) {
1386            // now also discard the children
1387            if (instance instanceof ISequenceInstance) {
1388                int index = 0;
1389                for (ITaskInstance childInstance : (ITaskInstanceList) instance) {
1390                    pathToInstance.add(childInstance.getTask(), index++);
1391                    discardTaskInstancesNotBelongingToTraversals
1392                        (childInstance, pathToInstance, similarTasks);
1393                    pathToInstance.removeLast();
1394                }
1395            }
1396            else if (instance instanceof IIterationInstance) {
1397                for (ITaskInstance childInstance : (ITaskInstanceList) instance) {
1398                    pathToInstance.add(childInstance.getTask(), 0);
1399                    discardTaskInstancesNotBelongingToTraversals
1400                        (childInstance, pathToInstance, similarTasks);
1401                    pathToInstance.removeLast();
1402                }
1403            }
1404            else if (instance instanceof IOptionalInstance) {
1405                ITaskInstance childInstance = ((IOptionalInstance) instance).getChild();
1406                if (childInstance != null) {
1407                    pathToInstance.add(childInstance.getTask(), 0);
1408                    discardTaskInstancesNotBelongingToTraversals
1409                        (childInstance, pathToInstance, similarTasks);
1410                    pathToInstance.removeLast();
1411                }
1412            }
1413            else if (instance instanceof ISelectionInstance) {
1414                ITaskInstance childInstance = ((ISelectionInstance) instance).getChild();
1415                pathToInstance.add(childInstance.getTask(), 0);
1416                discardTaskInstancesNotBelongingToTraversals
1417                    (childInstance, pathToInstance, similarTasks);
1418                pathToInstance.removeLast();
1419            }
1420        }
1421    }
1422
1423    /**
1424     *
1425     */
1426    private static boolean pathsMatch(TaskPath path1, TaskPath path2) {
1427        if (path1 == null) {
1428            return path2 == null;
1429        }
1430        else if ((path2 != null) && (path1.size() == path2.size())) {
1431            for (int i = 0; i < path1.size(); i++) {
1432                if (!path1.get(i).equals(path2.get(i))) {
1433                    return false;
1434                }
1435            }
1436            return true;
1437        }
1438        else {
1439            return false;
1440        }
1441    }
1442   
1443    /**
1444     *
1445     */
1446    private void removeSelectionChildIfNotUsedAnymoreInInstances(ISelection selection, ITask child) {
1447        boolean foundOtherInstanceWithChild = false;
1448       
1449        for (ITaskInstance instance : selection.getInstances()) {
1450            ITask candidate = ((ISelectionInstance) instance).getChild().getTask();
1451           
1452            if (candidate == child) {
1453                foundOtherInstanceWithChild = true;
1454                break;
1455            }
1456        }
1457       
1458        if (!foundOtherInstanceWithChild) {
1459            taskBuilder.removeChild(selection, child);
1460        }
1461
1462    }
1463
1464    /**
1465     *
1466     */
1467    private class RuleApplicationData {
1468
1469        /**
1470         *
1471         */
1472        private List<IUserSession> sessions;
1473       
1474        /**
1475         *
1476         */
1477        private ITaskModel taskModel;
1478
1479        /**
1480         *
1481         */
1482        private MostSimilarTaskDeterminer mostSimilarTasksDeterminer =
1483            new MostSimilarTaskDeterminer(identTaskHandlStrat.getTaskComparator());
1484       
1485        /**
1486         *
1487         */
1488        private List<SimilarTasks> mostSimilarTasks;
1489       
1490        /**
1491         *
1492         */
1493        private List<ITask> newTasks = new LinkedList<ITask>();
1494       
1495        /**
1496         *
1497         */
1498        private RuleApplicationResult ruleApplicationResult = new RuleApplicationResult();
1499       
1500        /**
1501         *
1502         */
1503        private RuleApplicationData(List<IUserSession> sessions) {
1504            this.sessions = sessions;
1505        }
1506
1507        /**
1508         *
1509         */
1510        boolean isSelfCreatedTask(ITask task) {
1511            for (ITask candidate : newTasks) {
1512                if (candidate == task) {
1513                    return true;
1514                }
1515            }
1516           
1517            return false;
1518        }
1519
1520        /**
1521         * @return the mostSimilarTasksDeterminer
1522         */
1523        private MostSimilarTaskDeterminer getMostSimilarTasksDeterminer() {
1524            return mostSimilarTasksDeterminer;
1525        }
1526
1527        /**
1528         *
1529         */
1530        private void markAsAlreadyCondensed(ITask task1, ITask task2) {
1531            mostSimilarTasksDeterminer.addComparisonToSkip(task1, task2);
1532        }
1533
1534        /**
1535         *
1536         */
1537        private RuleApplicationResult finalizeRuleApplicationResult() {
1538            for (ITask newTask : newTasks) {
1539                ruleApplicationResult.addNewlyCreatedTask(newTask);
1540               
1541                if (newTask instanceof IOptional) {
1542                    if (isSelfCreatedTask(((IOptional) newTask).getMarkedTask()) &&
1543                        (((IOptional) newTask).getMarkedTask() instanceof ISequence))
1544                    {
1545                        ruleApplicationResult.addNewlyCreatedTask
1546                            (((IOptional) newTask).getMarkedTask());
1547                    }
1548                }
1549                else if (newTask instanceof ISelection) {
1550                    for (ITask child : ((ISelection) newTask).getChildren()) {
1551                        if (isSelfCreatedTask(child) && (child instanceof ISequence)) {
1552                            ruleApplicationResult.addNewlyCreatedTask(child);
1553                        }
1554                    }
1555                }
1556            }
1557           
1558           
1559            return ruleApplicationResult;
1560        }
1561
1562        /**
1563         * @return the tree
1564         */
1565        private List<IUserSession> getSessions() {
1566            return sessions;
1567        }
1568
1569        /**
1570         * @return the taskModel
1571         */
1572        private ITaskModel getTaskModel() {
1573            return taskModel;
1574        }
1575
1576        /**
1577         * @param taskModel the taskModel to set
1578         */
1579        private void setTaskModel(ITaskModel taskModel) {
1580            this.taskModel = taskModel;
1581        }
1582
1583        /**
1584         * @return the orderedDiffLevels
1585         */
1586        private List<SimilarTasks> getMostSimilarTasks() {
1587            return mostSimilarTasks;
1588        }
1589
1590        /**
1591         * @param orderedDiffLevels the orderedDiffLevels to set
1592         */
1593        private void setMostSimilarTasks(List<SimilarTasks> mostSimilarTasks) {
1594            this.mostSimilarTasks = mostSimilarTasks;
1595        }
1596       
1597        /**
1598         *
1599         */
1600        private ITask ensureUnique(ITask task) {
1601            // replacements done in this rule are either optionals or selections. So focus on them
1602            if (task instanceof IOptional) {
1603                for (ITask newTask : newTasks) {
1604                    if (newTask instanceof IOptional) {
1605                        ITask child1 = ((IOptional) task).getMarkedTask();
1606                        ITask child2 = ((IOptional) newTask).getMarkedTask();
1607                        if (createdChildEquals(child1, child2)) {
1608                            // System.out.println("reusing optional " + newTask + " for " + task);
1609                            return newTask;
1610                        }
1611                    }
1612                }
1613            }
1614            else if (task instanceof ISelection) {
1615                for (ITask newTask : newTasks) {
1616                    if (newTask instanceof ISelection) {
1617                        List<ITask> children1 = ((ISelection) task).getChildren();
1618                        List<ITask> children2 = ((ISelection) newTask).getChildren();
1619                        if (createdSelectionChildrenEqual(children1, children2)) {
1620                            /*System.out.println("reusing selection " + newTask + " for " + task);
1621                           
1622                            System.out.println("---------------------------- existing task");
1623                            new TaskTreeEncoder().encode(newTask, System.out);
1624                           
1625                            System.out.println("---------------------------- completely new task");
1626                            new TaskTreeEncoder().encode(task, System.out);*/
1627                           
1628                            return newTask;
1629                        }
1630                    }
1631                }
1632            }
1633            else if (task instanceof ISequence) {
1634                List<ISequence> allRelevant = new ArrayList<>();
1635               
1636                for (ITask candidate : newTasks) {
1637                    if (candidate instanceof ISequence) {
1638                        allRelevant.add((ISequence) candidate);
1639                    }
1640                }
1641               
1642                for (ITask candidate : taskModel.getTasks()) {
1643                    if (candidate instanceof ISequence) {
1644                        allRelevant.add((ISequence) candidate);
1645                    }
1646                }
1647               
1648                for (ISequence candidate : allRelevant) {
1649                    List<ITask> children1 = ((ISequence) task).getChildren();
1650                    List<ITask> children2 = ((ISequence) candidate).getChildren();
1651
1652                    boolean equal = false;
1653                    if (children1.size() == children2.size()) {
1654                        equal = true;
1655                        for (int i = 0; i < children1.size(); i++) {
1656                            if (children1.get(i) != children2.get(i)) {
1657                                equal = false;
1658                                break;
1659                            }
1660                        }
1661                    }
1662
1663                    if (equal) {
1664                        // System.out.println("reusing sequence " + candidate + " for " + task);
1665                        return candidate;
1666                    }
1667                }
1668            }
1669           
1670            newTasks.add(task);
1671           
1672            return task;
1673        }
1674
1675        /**
1676         *
1677         */
1678        private boolean createdSelectionChildrenEqual(List<ITask> children1, List<ITask> children2) {
1679            if (children1.size() != children2.size()) {
1680                return false;
1681            }
1682           
1683            for (ITask child1 : children1) {
1684                boolean found = false;
1685               
1686                for (ITask child2 : children2) {
1687                    if (createdChildEquals(child1, child2)) {
1688                        found = true;
1689                        break;
1690                    }
1691                }
1692               
1693                if (!found) {
1694                    return false;
1695                }
1696            }
1697           
1698            return true;
1699        }
1700
1701        /**
1702         *
1703         */
1704        private boolean createdChildEquals(ITask child1, ITask child2) {
1705            if (child1 == child2) {
1706                return true;
1707            }
1708            else if (!isSelfCreatedTask(child1) && !isSelfCreatedTask(child2)) {
1709                // the simple comparison can not work if the tasks are not self created
1710                return false;
1711            }
1712            else if ((child1 instanceof ISequence) && (child2 instanceof ISequence)) {
1713                ISequence sequence1 = (ISequence) child1;
1714                ISequence sequence2 = (ISequence) child2;
1715               
1716                if (sequence1.getChildren().size() != sequence2.getChildren().size()) {
1717                    return false;
1718                }
1719               
1720                for (int i = 0; i < sequence1.getChildren().size(); i++) {
1721                    if (sequence1.getChildren().get(i) != sequence2.getChildren().get(i)) {
1722                        return false;
1723                    }
1724                }
1725               
1726                return true;
1727            }
1728            else {
1729                return false;
1730            }
1731        }
1732    }
1733   
1734    /**
1735     *
1736     */
1737    private static class FlattenInstruction {
1738       
1739        /**
1740         *
1741         */
1742        private enum Instruction { DONT_TRAVERSE, MAKE_OPTIONAL, MAKE_SELECTION, INTEGRATE_OPTIONAL };
1743
1744        /** */
1745        private TaskPath path;
1746
1747        /** */
1748        private TaskPath precedingPath;
1749
1750        /** */
1751        private Instruction instruction;
1752
1753        /** */
1754        private IOptional optional = null;
1755
1756        /** */
1757        private ISelection selection = null;
1758
1759        /** */
1760        private ITask selectedChild = null;
1761
1762        /**
1763         *
1764         */
1765        private FlattenInstruction(TaskPath path) {
1766            this.path = path;
1767            this.instruction = Instruction.DONT_TRAVERSE;
1768        }
1769
1770        /**
1771         *
1772         */
1773        private FlattenInstruction(TaskPath path, IOptional optional) {
1774            this.path = path;
1775            this.instruction = Instruction.MAKE_OPTIONAL;
1776            this.optional = optional;
1777        }
1778
1779        /**
1780         *
1781         */
1782        private FlattenInstruction(TaskPath path, ISelection selection, ITask selectedChild) {
1783            this.path = path;
1784            this.instruction = Instruction.MAKE_SELECTION;
1785            this.selection = selection;
1786            this.selectedChild = selectedChild;
1787        }
1788
1789        /**
1790         *
1791         */
1792        private FlattenInstruction(TaskPath  precedingPath,
1793                                   TaskPath  path,
1794                                   IOptional optional)
1795        {
1796            this.path = path;
1797            this.precedingPath = precedingPath;
1798            this.instruction = Instruction.INTEGRATE_OPTIONAL;
1799            this.optional = optional;
1800        }
1801       
1802        /**
1803         *
1804         */
1805        IOptional getOptional() {
1806            return optional;
1807        }
1808
1809
1810        /**
1811         * @return the selection
1812         */
1813        ISelection getSelection() {
1814            return selection;
1815        }
1816
1817        /**
1818         * @return the selectedChild
1819         */
1820        ITask getSelectedChild() {
1821            return selectedChild;
1822        }
1823
1824        /**
1825         * @return the instruction
1826         */
1827        Instruction getInstruction() {
1828            return instruction;
1829        }
1830
1831        /**
1832         * @return the precedingPath
1833         */
1834        TaskPath getPrecedingPath() {
1835            return precedingPath;
1836        }
1837
1838        /**
1839         *
1840         */
1841        boolean matches(TaskPath path) {
1842            return pathsMatch(this.path, path);
1843        }
1844       
1845
1846//        /**
1847//         *
1848//         */
1849//        void dump(PrintStream out) {
1850//            for (int i = 0; i < path.size(); i++) {
1851//                out.print("-->");
1852//                out.print(path.getTask(i).getId());
1853//                out.print('(');
1854//                out.print(path.get(i).getIndex());
1855//                out.print(')');
1856//            }
1857//           
1858//            out.print("  ");
1859//            out.print(instruction);
1860//           
1861//            out.println();
1862//        }
1863       
1864    }
1865
1866    /**
1867     *
1868     */
1869    private class MarkingTemporalRelationshipHarmonizer
1870        extends DefaultTaskInstanceTraversingVisitor
1871    {
1872        /** */
1873        final Map<ITask, IIteration> harmonizedIterations = new HashMap<>();
1874       
1875        /** */
1876        final Map<ITask, IOptional> harmonizedOptionals = new HashMap<>();
1877       
1878        /** */
1879        private boolean somethingChanged = false;
1880       
1881        /**
1882         *
1883         */
1884        @Override
1885        public void visit(IIterationInstance iterationInstance) {
1886            // visit the children
1887            super.visit(iterationInstance);
1888           
1889            // there may have been a model update at the children. If so, set the
1890            // new marked task
1891            IIteration iteration = iterationInstance.getIteration();
1892            ITask newChildTask = iterationInstance.get(0).getTask();
1893           
1894            if (newChildTask != iteration.getMarkedTask()) {
1895                taskBuilder.setMarkedTask(iteration, newChildTask);
1896            }
1897           
1898            // check, if there is a harmonized iteration
1899            IIteration harmonizedIteration =
1900                harmonizedIterations.get(iteration.getMarkedTask());
1901           
1902            if ((harmonizedIteration != null) && (iteration != harmonizedIteration)) {
1903                // there is a harmonized iteration --> set it as new task
1904                /*System.out.println("harmonizing iteration of " +
1905                                   iteration.getMarkedTask() + " from " + iteration +
1906                                   " to " + harmonizedIteration);*/
1907                taskBuilder.setTask(iterationInstance, harmonizedIteration);
1908                somethingChanged = true;
1909            }
1910            else if (harmonizedIteration == null) {
1911                // remember this iteration as the harmonized one
1912                harmonizedIterations.put(iteration.getMarkedTask(), iteration);
1913            }
1914        }
1915       
1916        /**
1917         *
1918         */
1919        @Override
1920        public void visit(IOptionalInstance optionalInstance) {
1921            // visit the children
1922            super.visit(optionalInstance);
1923           
1924            IOptional optional = optionalInstance.getOptional();
1925
1926            if (optionalInstance.getChild() != null) {
1927                // there may have been a model update at the child. If so, set the
1928                // new marked task
1929                ITask newChildTask = optionalInstance.getChild().getTask();
1930               
1931                if (newChildTask != optional.getMarkedTask()) {
1932                    taskBuilder.setMarkedTask(optional, newChildTask);
1933                }
1934            }
1935
1936            // check, if there is a harmonized optional
1937            IOptional harmonizedOptional =
1938                harmonizedOptionals.get(optional.getMarkedTask());
1939
1940            if ((harmonizedOptional != null) && (optional != harmonizedOptional)) {
1941                // there is a harmonized optional --> set it as new task
1942                /*System.out.println("harmonizing optional of " +
1943                                   optional.getMarkedTask() + " from " + optional +
1944                                   " to " + harmonizedOptional);*/
1945                taskBuilder.setTask(optionalInstance, harmonizedOptional);
1946                somethingChanged = true;
1947            }
1948            else if (harmonizedOptional == null) {
1949                // remember this optional as the harmonized one
1950                harmonizedOptionals.put(optional.getMarkedTask(), optional);
1951            }
1952        }
1953
1954        /**
1955         *
1956         */
1957        @Override
1958        public void visit(ISelectionInstance selectionInstance) {
1959            ITask childTaskBeforeUpdate = selectionInstance.getChild().getTask();
1960           
1961            super.visit(selectionInstance);
1962           
1963            ITask childTaskAfterUpdate = selectionInstance.getChild().getTask();
1964           
1965            if (childTaskBeforeUpdate != childTaskAfterUpdate) {
1966                // update the selection model if required.
1967                ISelection selection = selectionInstance.getSelection();
1968                removeSelectionChildIfNotUsedAnymoreInInstances(selection, childTaskBeforeUpdate);
1969               
1970                // remove and add the child to ensure to have it only once
1971                taskBuilder.removeChild(selection, childTaskAfterUpdate);
1972                taskBuilder.addChild(selection, childTaskAfterUpdate);
1973                somethingChanged = true;
1974            }
1975        }
1976
1977        /**
1978         *
1979         */
1980        @Override
1981        public void visit(ISequenceInstance sequenceInstance) {
1982            ISequence sequence = sequenceInstance.getSequence();
1983            int childIndex = 0;
1984           
1985            for (ITaskInstance child : sequenceInstance) {
1986                child.accept(this);
1987               
1988                ITask newChildTask = child.getTask();
1989               
1990                if (sequence.getChildren().get(childIndex) != newChildTask) {
1991                    taskBuilder.setChild(sequenceInstance.getSequence(), childIndex,
1992                                         newChildTask);
1993                    somethingChanged = true;
1994                }
1995               
1996                childIndex++;
1997            }
1998        }
1999       
2000    }
2001   
2002
2003    /**
2004     *
2005     */
2006    private class SingleIterationExecutionsIntegrator
2007        extends DefaultTaskInstanceTraversingVisitor
2008    {
2009
2010        /** */
2011        private Map<ITask, IIteration> iterations;
2012       
2013        /** */
2014        private boolean somethingChanged = false;
2015
2016        /**
2017         *
2018         */
2019        public SingleIterationExecutionsIntegrator(Map<ITask, IIteration> iterations) {
2020            this.iterations = iterations;
2021        }
2022
2023        /* (non-Javadoc)
2024         * @see DefaultTaskInstanceTraversingVisitor#visit(IOptionalInstance)
2025         */
2026        @Override
2027        public void visit(IOptionalInstance optionalInstance) {
2028            super.visit(optionalInstance);
2029
2030            if (optionalInstance.getChild() != null) {
2031                IIteration iteration = iterations.get(optionalInstance.getChild().getTask());
2032
2033                if (iteration != null) {
2034                    taskBuilder.setMarkedTask(optionalInstance.getOptional(), iteration);
2035                    IIterationInstance replacement = taskFactory.createNewTaskInstance(iteration);
2036                    taskBuilder.addChild(replacement, optionalInstance.getChild());
2037                    taskBuilder.setChild(optionalInstance, replacement);
2038                    somethingChanged = true;
2039                }
2040            }
2041        }
2042
2043        /* (non-Javadoc)
2044         * @see DefaultTaskInstanceTraversingVisitor#visit(ISelectionInstance)
2045         */
2046        @Override
2047        public void visit(ISelectionInstance selectionInstance) {
2048            super.visit(selectionInstance);
2049           
2050            ITask usedChildTask = selectionInstance.getChild().getTask();
2051            IIteration iteration = iterations.get(usedChildTask);
2052           
2053            if (iteration != null) {
2054                // extend the selection model
2055                // remove and add the child to ensure to have it only once
2056                ISelection selection = selectionInstance.getSelection();
2057                taskBuilder.removeChild(selection, iteration);
2058                taskBuilder.addChild(selection, iteration);
2059               
2060                // update the instance
2061                IIterationInstance replacement = taskFactory.createNewTaskInstance(iteration);
2062                taskBuilder.addChild(replacement, selectionInstance.getChild());
2063                taskBuilder.setChild(selectionInstance, replacement);
2064               
2065                // update the selection model if required.
2066                removeSelectionChildIfNotUsedAnymoreInInstances(selection, usedChildTask);
2067               
2068                somethingChanged = true;
2069            }
2070
2071        }
2072
2073        /* (non-Javadoc)
2074         * @see DefaultTaskInstanceTraversingVisitor#visit(ISequenceInstance)
2075         */
2076        @Override
2077        public void visit(ISequenceInstance sequenceInstance) {
2078            int index = 0;
2079            while (index < sequenceInstance.size()) {
2080                ITaskInstance child = sequenceInstance.get(index);
2081                child.accept(this);
2082               
2083                IIteration iteration = iterations.get(child.getTask());
2084               
2085                if (iteration != null) {
2086                    taskBuilder.setChild(sequenceInstance.getSequence(), index, iteration);
2087                    IIterationInstance replacement = taskFactory.createNewTaskInstance(iteration);
2088                    taskBuilder.addChild(replacement, child);
2089                    taskBuilder.setTaskInstance(sequenceInstance, index, replacement);
2090                    somethingChanged = true;
2091                }
2092               
2093                index++;
2094            }
2095        }
2096
2097    }
2098
2099    /**
2100     *
2101     */
2102    private class SubsequentIdenticalChildrenMerger {
2103       
2104        /** */
2105        private boolean somethingChanged = false;
2106
2107        /**
2108         *
2109         */
2110        private void mergeSubsequentIdenticalTasks(ITask task) {
2111            TaskComparator comparator = identTaskHandlStrat.getTaskComparator();
2112
2113            if (task instanceof ISequence) {
2114                // merge the children
2115                mergeSubsequentIdenticalTasks((ISequence) task);
2116               
2117                int index = 0;
2118               
2119                while (index < ((ISequence) task).getChildren().size()) {
2120                    ITask child = ((ISequence) task).getChildren().get(index);
2121
2122                    List<ITaskInstance> childInstances = new LinkedList<>();
2123                   
2124                    for (ITaskInstance instance : task.getInstances()) {
2125                        ITaskInstance childInstance = ((ISequenceInstance) instance).get(index);
2126                        if (comparator.equals(childInstance.getTask(), child)) {
2127                            childInstances.add(childInstance);
2128                        }
2129                    }
2130                   
2131                    Map<ITaskInstance, ITaskInstance> childInstanceReplacements = new HashMap<>();
2132                   
2133                    ITask replacementTask =
2134                        getReplacements(child, childInstances, childInstanceReplacements);
2135                       
2136                    if (replacementTask != null) {
2137                        taskBuilder.setChild((ISequence) task, index, replacementTask);
2138
2139                        for (ITaskInstance instance : task.getInstances()) {
2140                            ITaskInstance childInstance = ((ISequenceInstance) instance).get(index);
2141                            ITaskInstance replacement = childInstanceReplacements.get(childInstance);
2142
2143                            if (replacement != null) {
2144                                taskBuilder.setTaskInstance
2145                                    ((ISequenceInstance) instance, index, replacement);
2146                            }
2147                        }
2148                       
2149                        somethingChanged = true;
2150                    }
2151                   
2152                    index++;
2153                }
2154            }
2155            else if (task instanceof ISelection) {
2156                // it could have children being marking temporal relationship of a sequence of
2157                // interest. If so, adapt the model and the instances
2158                int index = 0;
2159               
2160                while (index < ((ISelection) task).getChildren().size()) {
2161                    ITask child = ((ISelection) task).getChildren().get(index);
2162                    List<ITaskInstance> childInstances = new LinkedList<>();
2163                   
2164                    for (ITaskInstance instance : task.getInstances()) {
2165                        ITaskInstance childInstance = ((ISelectionInstance) instance).getChild();
2166                        if (comparator.equals(childInstance.getTask(), child)) {
2167                            childInstances.add(childInstance);
2168                        }
2169                    }
2170                   
2171                    Map<ITaskInstance, ITaskInstance> childInstanceReplacements = new HashMap<>();
2172                   
2173                    ITask replacementTask =
2174                        getReplacements(child, childInstances, childInstanceReplacements);
2175                   
2176                    if (replacementTask != null) {
2177                        taskBuilder.removeChild((ISelection) task, child);
2178                        taskBuilder.addChild((ISelection) task, replacementTask);
2179
2180                        for (ITaskInstance instance : task.getInstances()) {
2181                            ITaskInstance childInstance = ((ISelectionInstance) instance).getChild();
2182                            ITaskInstance replacement = childInstanceReplacements.get(childInstance);
2183
2184                            if (replacement != null) {
2185                                taskBuilder.setChild(((ISelectionInstance) instance), replacement);
2186                            }
2187                        }
2188                       
2189                        // start from the beginning as the children order may have changed
2190                        index = 0;
2191                        somethingChanged = true;
2192                    }
2193                    else {
2194                        index++;
2195                    }
2196                }
2197            }
2198        }
2199
2200        /**
2201         *
2202         */
2203        private ITask getReplacements(ITask                             child,
2204                                      List<ITaskInstance>               childInstances,
2205                                      Map<ITaskInstance, ITaskInstance> childInstanceReplacements)
2206        {
2207            final TaskComparator comparator = identTaskHandlStrat.getTaskComparator();
2208            ITask candidate = child;
2209            boolean isOptional = false;
2210           
2211            while (candidate instanceof IMarkingTemporalRelationship) {
2212                if (candidate instanceof IOptional) {
2213                    isOptional = true;
2214                }
2215                candidate = ((IMarkingTemporalRelationship) candidate).getMarkedTask();
2216            }
2217           
2218            if ((candidate instanceof IEventTask) || (candidate instanceof ISelection) ||
2219                (((ISequence) candidate).getChildren().size() != 1))
2220            {
2221                // no sequence of interest as it has more than one children
2222                return null;
2223            }
2224           
2225            // found a sequence to be handled. The sequence has a single child and this will
2226            // be either an optional of an iteration of the single child, or an iteration of
2227            // the single child.
2228            final ISequence sequenceWithSingleChild = (ISequence) candidate;
2229           
2230            // determine all instances of the sequence rooted by the provided parent instance
2231            ITask singleChild = sequenceWithSingleChild.getChildren().get(0);
2232            IOptional optional = null;
2233
2234            if (singleChild instanceof IOptional) {
2235                isOptional = true;
2236                optional = (IOptional) singleChild;
2237                singleChild = optional.getMarkedTask();
2238            }
2239
2240            IIteration iteration = (IIteration) singleChild;
2241
2242            if (isOptional && (optional == null)) {
2243                optional = taskFactory.createNewOptional();
2244                taskBuilder.setMarkedTask(optional, iteration);
2245            }
2246
2247            for (ITaskInstance childInstance : childInstances) {
2248                // get all instances of the single child of the sequence of interest which belong to
2249                // the a child instance to be replaced and calculate a replacement
2250                final List<ITaskInstance> singleChildInstances = new LinkedList<>();
2251                final ITask taskToSearchFor = singleChild;
2252               
2253                childInstance.accept(new DefaultTaskInstanceTraversingVisitor() {
2254                    @Override
2255                    public void visit(ISelectionInstance selectionInstance) {
2256                        if (comparator.equals(selectionInstance.getTask(), taskToSearchFor)) {
2257                            singleChildInstances.add(selectionInstance);
2258                        }
2259                    }
2260
2261                    @Override
2262                    public void visit(ISequenceInstance sequenceInstance) {
2263                        if (comparator.equals(sequenceInstance.getTask(), taskToSearchFor)) {
2264                            singleChildInstances.add(sequenceInstance);
2265                        }
2266                        else if (comparator.equals(sequenceInstance.getTask(),
2267                                                   sequenceWithSingleChild))
2268                        {
2269                            super.visit(sequenceInstance);
2270                        }
2271                    }
2272                   
2273                });
2274               
2275                IIterationInstance iterationInstance = taskFactory.createNewTaskInstance(iteration);
2276                for (ITaskInstance singleChildInstance : singleChildInstances) {
2277                    taskBuilder.addTaskInstance(iterationInstance, singleChildInstance);
2278                }
2279               
2280                ITaskInstance replacement = iterationInstance;
2281               
2282                if (isOptional) {
2283                    IOptionalInstance optionalInstance = taskFactory.createNewTaskInstance(optional);
2284                    taskBuilder.setChild(optionalInstance, replacement);
2285                    replacement = optionalInstance;
2286                }
2287               
2288                childInstanceReplacements.put(childInstance, replacement);
2289            }
2290           
2291            if (isOptional) {
2292                return optional;
2293            }
2294            else {
2295                return iteration;
2296            }
2297        }
2298
2299        /**
2300         *
2301         */
2302        private void mergeSubsequentIdenticalTasks(ISequence sequence) {
2303            int index = 0;
2304            TaskComparator comparator = identTaskHandlStrat.getTaskComparator();
2305           
2306            while (index < (sequence.getChildren().size() - 1)) {
2307                ITask child1 = sequence.getChildren().get(index);
2308                ITask child2 = sequence.getChildren().get(index + 1);
2309               
2310                // get the actual first task
2311                ITask task1 = child1;
2312               
2313                while (task1 instanceof IMarkingTemporalRelationship) {
2314                    task1 = ((IMarkingTemporalRelationship) task1).getMarkedTask();
2315                }
2316               
2317                // get the actual second task
2318                ITask task2 = child2;
2319               
2320                while (task2 instanceof IMarkingTemporalRelationship) {
2321                    task2 = ((IMarkingTemporalRelationship) task2).getMarkedTask();
2322                }
2323               
2324                if (comparator.equals(task1, task2)) {
2325                    // determine if the task is optional
2326                    boolean isOptional = false;
2327                   
2328                    if (child1 instanceof IOptional) {
2329                        child1 = ((IOptional) child1).getMarkedTask();
2330                        isOptional = true;
2331                    }
2332                   
2333                    if (child2 instanceof IOptional) {
2334                        child2 = ((IOptional) child2).getMarkedTask();
2335                        isOptional = true;
2336                    }
2337                   
2338                    if (child1 instanceof IIteration) {
2339                        child1 = ((IIteration) child1).getMarkedTask();
2340                    }
2341                   
2342                    if (child2 instanceof IIteration) {
2343                        child2 = ((IIteration) child2).getMarkedTask();
2344                    }
2345                   
2346                    IIteration iteration = taskFactory.createNewIteration();
2347                    taskBuilder.setMarkedTask(iteration, task1);
2348                   
2349                    IOptional optional = null;
2350                   
2351                    if (isOptional) {
2352                        optional = taskFactory.createNewOptional();
2353                        taskBuilder.setMarkedTask(optional, iteration);
2354                        taskBuilder.setChild(sequence, index, optional);
2355                    }
2356                    else {
2357                        taskBuilder.setChild(sequence, index, iteration);
2358                    }
2359                   
2360                    taskBuilder.removeChild(sequence, index + 1);
2361                   
2362                    for (ITaskInstance instance : sequence.getInstances()) {
2363                        mergeSubsequentIdenticalInstances((ISequenceInstance) instance, index,
2364                                                          iteration, optional);
2365                    }
2366                   
2367                    somethingChanged = true;
2368                }
2369                else {
2370                    index++;
2371                }
2372            }
2373        }
2374
2375        /**
2376         *
2377         */
2378        private void mergeSubsequentIdenticalInstances(ITaskInstanceList list) {
2379            int index = 0;
2380            TaskComparator comparator = identTaskHandlStrat.getTaskComparator();
2381           
2382            while (index < (list.size() - 1)) {
2383                boolean isOptional = false;
2384                ITaskInstance instance1 = list.get(index);
2385                ITaskInstance instance2 = list.get(index + 1);
2386               
2387                // get the actual first task
2388                ITask task1 = instance1.getTask();
2389               
2390                while (task1 instanceof IMarkingTemporalRelationship) {
2391                    if (task1 instanceof IOptional) {
2392                        isOptional = true;
2393                    }
2394                   
2395                    task1 = ((IMarkingTemporalRelationship) task1).getMarkedTask();
2396                }
2397               
2398                // get the actual second task
2399                ITask task2 = instance2.getTask();
2400               
2401                while (task2 instanceof IMarkingTemporalRelationship) {
2402                    if (task2 instanceof IOptional) {
2403                        isOptional = true;
2404                    }
2405                   
2406                    task2 = ((IMarkingTemporalRelationship) task2).getMarkedTask();
2407                }
2408               
2409                if (comparator.equals(task1, task2)) {
2410                    IIteration iteration = taskFactory.createNewIteration();
2411                    IOptional optional = null;
2412                   
2413                    if (isOptional) {
2414                        optional = taskFactory.createNewOptional();
2415                        taskBuilder.setMarkedTask(optional, iteration);
2416                    }
2417                   
2418                    mergeSubsequentIdenticalInstances(list, index, iteration, optional);
2419                }
2420               
2421                index++;
2422            }
2423        }
2424       
2425        /**
2426         *
2427         */
2428        private void mergeSubsequentIdenticalInstances(ITaskInstanceList list,
2429                                                       int               index,
2430                                                       IIteration        iteration,
2431                                                       IOptional         optional)
2432        {
2433            ITaskInstance instance1 = list.get(index);
2434            ITaskInstance instance2 = list.get(index + 1);
2435            List<ITaskInstance> equalInstances = new LinkedList<>();
2436           
2437            if (instance1 instanceof IOptionalInstance) {
2438                taskBuilder.discardTaskInstance(instance1);
2439                instance1 = ((IOptionalInstance) instance1).getChild();
2440            }
2441           
2442            if (instance2 instanceof IOptionalInstance) {
2443                taskBuilder.discardTaskInstance(instance2);
2444                instance2 = ((IOptionalInstance) instance2).getChild();
2445            }
2446           
2447            if (instance1 instanceof IIterationInstance) {
2448                for (ITaskInstance child : (IIterationInstance) instance1) {
2449                    equalInstances.add(child);
2450                }
2451               
2452                taskBuilder.discardTaskInstance(instance1);
2453            }
2454            else if (instance1 != null) {
2455                equalInstances.add(instance1);
2456            }
2457           
2458            if (instance2 instanceof IIterationInstance) {
2459                for (ITaskInstance child : (IIterationInstance) instance2) {
2460                    equalInstances.add(child);
2461                }
2462               
2463                taskBuilder.discardTaskInstance(instance2);
2464            }
2465            else if (instance2 != null) {
2466                equalInstances.add(instance2);
2467            }
2468           
2469            ITaskInstance replacement = taskFactory.createNewTaskInstance(iteration);
2470           
2471            for (ITaskInstance equalInstance : equalInstances) {
2472                taskBuilder.addChild((IIterationInstance) replacement, equalInstance);
2473            }
2474           
2475            if (optional != null) {
2476                IOptionalInstance optionalInstance = taskFactory.createNewTaskInstance(optional);
2477               
2478                taskBuilder.setChild(optionalInstance, replacement);
2479                replacement = optionalInstance;
2480            }
2481           
2482            taskBuilder.setTaskInstance(list, index, replacement);
2483            taskBuilder.removeTaskInstance(list, index + 1);
2484        }
2485    }
2486}
Note: See TracBrowser for help on using the repository browser.