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

Last change on this file since 1854 was 1854, checked in by pharms, 10 years ago
  • only sequences are now compared
  • added check that only one replacement task is calculated per merge
  • corrected harmonization of iterations and optionals being parents of merge results
File size: 75.6 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.IIteration;
30import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance;
31import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship;
32import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptional;
33import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptionalInstance;
34import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
35import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance;
36import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
37import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance;
38import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
39import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskBuilder;
40import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskFactory;
41import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
42import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstanceList;
43import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
44import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
45import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskPath;
46import de.ugoe.cs.util.console.Console;
47import difflib.Chunk;
48import difflib.Delta;
49
50/**
51 * <p>
52 * This rule performs a condensing of a task tree. It searches for similar tasks and merges them
53 * if possible in to one task with different execution variants. Hence, this rule detects selections
54 * and optionals.
55 * </p>
56 * <p>
57 * The similarity of tasks is determined by comparing task traversals. A task traversal is an
58 * ordered list of the leaf nodes of a task. This is similar to performing a minimal execution of
59 * the task. The task traversals of two tasks are compared using string comparison algorithms. The
60 * less differences the lists have, the more similar they are.
61 * </p>
62 * <p>
63 * If two tasks are similar, they are merged. Merging is done based on the differences between
64 * the task traversals. Two tasks are merged based on their instances. First, all instances of both
65 * tasks are flattened. Instances of selections or commonalities of both tasks stay unflattened.
66 * Then the lists resulting from this flattening are extended with instances of optionals and
67 * selections which are introduced where required. Finally, the instances are put together to a
68 * single task again by applying the {@link SequenceForTaskDetectionRule} on them.
69 * </p>
70 * <p>
71 * Merging has to consider several specific situations. E.g., tasks may look similar but due to
72 * iterations, they can not be merged correctly. Here, the rule ensures, that these so called
73 * interleaving iterations are detected, not traversed when traversing a task and its instances,
74 * and, therefore, preserved.
75 * </p>
76 * <p>
77 * All details about this rule are described in the extended ACHI2014 paper of Harms "Trace-based
78 * task tree generation". The extended paper was submitted to the IntSys IARIA Journal.
79 * </p>
80 *
81 * @author Patrick Harms
82 */
83class CondenseSimilarTasksRule implements ISessionScopeRule {
84
85    /**
86     * <p>
87     * the task factory to be used for creating substructures for the temporal
88     * relationships identified during rule application
89     * </p>
90     */
91    private ITaskFactory taskFactory;
92    /**
93     * <p>
94     * the task builder to be used for creating substructures for the temporal relationships
95     * identified during rule application
96     * </p>
97     */
98    private ITaskBuilder taskBuilder;
99
100    /**
101     * <p>
102     * the task handling strategy to be used for comparing tasks during iteration detection an trie
103     * generation, i.e., after the tasks are harmonized
104     * </p>
105     */
106    private TaskHandlingStrategy identTaskHandlStrat;
107   
108    /**
109     * <p>
110     * instantiates the rule and initializes it with a task equality to be considered when
111     * comparing tasks as well as a task factory and builder to be used for creating task
112     * structures.
113     * </p>
114     *
115     * @param minimalTaskEquality the task equality to be considered when comparing tasks
116     * @param taskFactory         the task factory to be used for creating substructures
117     * @param taskBuilder         the task builder to be used for creating substructures
118     */
119    CondenseSimilarTasksRule(TaskEquality minimalTaskEquality,
120                             ITaskFactory taskFactory,
121                             ITaskBuilder taskBuilder)
122    {
123        this.taskFactory = taskFactory;
124        this.taskBuilder = taskBuilder;
125       
126        this.identTaskHandlStrat = new TaskHandlingStrategy(TaskEquality.IDENTICAL);
127    }
128
129    /* (non-Javadoc)
130     * @see java.lang.Object#toString()
131     */
132    @Override
133    public String toString() {
134        return "CondenseSimilarTasksRule";
135    }
136
137    /* (non-Javadoc)
138     * @see de.ugoe.cs.autoquest.tasktrees.temporalrelation.ISessionScopeRule#apply(java.util.List)
139     */
140    @Override
141    public RuleApplicationResult apply(List<IUserSession> sessions) {
142       
143        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
144        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
145        /*final List<IEventTaskInstance> formerInstances = new ArrayList<>();
146        final List<IEventTaskInstance> newInstances = new ArrayList<>();
147       
148        try {
149            ITaskInstanceVisitor visitor = new DefaultTaskInstanceTraversingVisitor() {
150                @Override
151                public void visit(IEventTaskInstance eventTaskInstance) {
152                    formerInstances.add(eventTaskInstance);
153                }
154            };
155           
156            PrintStream out = new PrintStream(new FileOutputStream(new File("01.out")));
157           
158            for (IUserSession session : sessions) {
159                new TaskTreeEncoder().encode(session, out, null);
160               
161                for (ITaskInstance instance : session) {
162                    instance.accept(visitor);
163                }
164            }
165            out.close();
166        }
167        catch (FileNotFoundException e) {
168            e.printStackTrace();
169        }
170       
171        PrintStream fileout = null;
172        try {
173            fileout = new PrintStream("sysout.txt");
174        }
175        catch (FileNotFoundException e1) {
176            e1.printStackTrace();
177        }
178       
179        PrintStream origOut = System.out;
180        if (fileout != null) {
181            System.setOut(fileout);
182        }*/
183       
184        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
185        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
186       
187       
188        RuleApplicationData appData = new RuleApplicationData(sessions);
189       
190        do {
191            appData.setTaskModel(taskFactory.createTaskModel(sessions));
192            appData.setMostSimilarTasks(null);
193           
194            // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
195            // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>
196            // for (ITask task : appData.getTaskModel().getTasks()) {
197            //     if (task.getInstances().size() <= 0) {
198            //         throw new RuntimeException("task " + task + " has no instances anymore");
199            //     }
200            //     
201            //     try {
202            //         new TaskTreeValidator().validate(task);
203            //     }
204            //     catch (Exception e) {
205            //         new TaskTreeEncoder().encode(task, System.err);
206            //     }
207            // }
208            //
209            // new TaskTreeValidator().validate(sessions);
210           
211            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
212            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<
213
214
215            Console.println("condensing " + appData.getTaskModel().getTasks().size() + " tasks");
216
217            getMostSimilarTasks(appData);
218
219            if (appData.getMostSimilarTasks() != null) {
220                for (SimilarTasks mostSimilarTask : appData.getMostSimilarTasks()) {
221                    handleSimilarTasks(mostSimilarTask, appData);
222                    appData.markAsAlreadyCondensed
223                        (mostSimilarTask.getLeftHandSide(), mostSimilarTask.getRightHandSide());
224                }
225            }
226           
227        }
228        while (appData.getMostSimilarTasks() != null);
229       
230       
231        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
232        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
233        /*try {
234            ITaskInstanceVisitor visitor = new DefaultTaskInstanceTraversingVisitor() {
235                @Override
236                public void visit(IEventTaskInstance eventTaskInstance) {
237                    newInstances.add(eventTaskInstance);
238                }
239            };
240           
241            PrintStream out = new PrintStream(new FileOutputStream(new File("02.out")));
242           
243            for (IUserSession session : sessions) {
244                new TaskTreeEncoder().encode(session, out, null);
245               
246                for (ITaskInstance instance : session) {
247                    instance.accept(visitor);
248                }
249            }
250            out.close();
251        }
252        catch (FileNotFoundException e) {
253            e.printStackTrace();
254        }
255       
256        System.out.println("sizes  " + formerInstances.size() + "  " + newInstances.size());
257        for (int i = 0; i < newInstances.size(); i++) {
258            if (formerInstances.get(i) != newInstances.get(i)) {
259                System.out.println(i + "  " + formerInstances.get(i) + "  " + newInstances.get(i));
260            }
261        }
262       
263        System.setOut(origOut);
264        fileout.close();*/
265        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
266        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
267
268       
269        return appData.finalizeRuleApplicationResult();
270    }
271
272    /**
273     *
274     */
275    private void getMostSimilarTasks(RuleApplicationData appData) {
276        // determine the list of interesting tasks
277        Collection<ITask> allTasks = appData.getTaskModel().getTasks();
278        Iterator<ITask> taskIterator = allTasks.iterator();
279        List<ITask> taskList = new ArrayList<ITask>(allTasks.size());
280       
281        while (taskIterator.hasNext()) {
282            ITask task = taskIterator.next();
283           
284            // only Sequences need to be compared with each other. Iterations differ only in their
285            // child, i.e., in the child sequences.
286            if (task instanceof ISequence) {
287                taskList.add(task);
288            }
289        }
290       
291        if (taskList.size() > 1) {
292            List<SimilarTasks> mostSimilarTasks =
293                appData.getMostSimilarTasksDeterminer().getMostSimilarTasks
294                    (taskList, appData.getTaskModel());
295           
296            if (mostSimilarTasks.size() > 0) {
297                appData.setMostSimilarTasks(mostSimilarTasks);
298            }
299        }
300    }
301
302    /**
303     *
304     */
305    private void handleSimilarTasks(SimilarTasks similarTasks, RuleApplicationData appData) {
306        // we need at least one instance per similar task. If not, it will not work and also
307        // does not make sense. No instances anymore can be caused by merging and hence
308        // discarding tasks in preceding merges.
309       
310        // similarTasks.dump(System.out);
311       
312        if ((similarTasks.getLeftHandSide().getInstances().size() <= 0) ||
313            (similarTasks.getRightHandSide().getInstances().size() <= 0))
314        {
315            /*System.out.println("a task exists that has no instances");
316            System.out.println(similarTasks.getLeftHandSide() + "  " +
317                               similarTasks.getLeftHandSide().getInstances().size());
318            System.out.println(similarTasks.getRightHandSide() + "  " +
319                               similarTasks.getRightHandSide().getInstances().size());*/
320           
321            throw new RuntimeException
322                ("one and the same task seems to belong to several similar tasks");
323        }
324       
325        similarTasks = SimilarTasks.getMergableLevelOfSimilarity
326            (similarTasks, identTaskHandlStrat.getTaskComparator());
327       
328        if (similarTasks == null) {
329            // this may happen, if no mergable level of similarity can be found
330            return;
331        }
332       
333        // similarTasks.dump(System.out);
334
335        List<FlattenInstruction> flattenInstructions =
336            getFlattenInstructions(similarTasks, appData);
337       
338        // for (FlattenInstruction instruction : flattenInstructions) {
339        //     instruction.dump(System.out);
340        // }
341       
342        int noOfFlattenedInstances = similarTasks.getLeftHandSide().getInstances().size() +
343            similarTasks.getRightHandSide().getInstances().size();
344       
345        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
346        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
347        List<TaskPath> excludes = new ArrayList<TaskPath>();
348       
349        for (FlattenInstruction instruction : flattenInstructions) {
350            //System.out.println("exclude " + instruction.path);
351            excludes.add(instruction.path);
352        }
353        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
354        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
355
356        Map<ITaskInstance, IUserSession> flattenedSessions =
357            new HashMap<ITaskInstance, IUserSession>(noOfFlattenedInstances);
358        flattenInstances
359            (similarTasks.getLeftHandSide(), flattenInstructions, flattenedSessions, excludes);
360        flattenInstances
361            (similarTasks.getRightHandSide(), flattenInstructions, flattenedSessions, excludes);
362
363        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
364        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
365        /*for (IUserSession session : flattenedSessions.values()) {
366            System.out.println("user session {");
367           
368            for (ITaskInstance instance : session) {
369                System.out.println(instance);
370            }
371           
372            System.out.println("}");
373        }*/
374        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
375        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
376       
377        List<IUserSession> flattenedSessionList =
378            new LinkedList<IUserSession>(flattenedSessions.values());
379       
380        new SequenceForTaskDetectionRule
381            (TaskEquality.IDENTICAL, taskFactory, taskBuilder).apply(flattenedSessionList);
382       
383        Map<ITaskInstance, ITaskInstance> replacements = new HashMap<ITaskInstance, ITaskInstance>();
384        ITask replacementTask = null;
385       
386        for (Map.Entry<ITaskInstance, IUserSession> entry : flattenedSessions.entrySet()) {
387            // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
388            // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
389            // the user sessions were sufficiently equal to have now only one common task as child
390           
391            if (entry.getValue().size() != 1) {
392                //new TaskTreeEncoder().encode(entry.getValue(), System.out, excludes);
393                throw new RuntimeException("flattened sessions were not combined as expected");
394            }
395           
396            if (replacementTask == null) {
397                replacementTask = entry.getValue().get(0).getTask();
398            }
399            else if (replacementTask != entry.getValue().get(0).getTask()) {
400                throw new RuntimeException("two separate replacement tasks were calculated as " +
401                                           "replacement, one for both tasks to be merged");
402            }
403           
404            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
405            // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
406           
407            replacements.put(entry.getKey(), entry.getValue().get(0));
408        }
409       
410        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
411        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
412        // ((Task) replacementTask).setDescription
413        //     (replacementTask + " calculated as full replacement for " +
414        //      similarTasks.getLeftHandSide() + " and " + similarTasks.getRightHandSide());
415   
416        int allInstances = similarTasks.getLeftHandSide().getInstances().size() +
417            similarTasks.getRightHandSide().getInstances().size();
418       
419        if (replacements.size() != allInstances) {
420            throw new RuntimeException("number of replacements does not match number of instances");
421        }
422       
423        /*if (replacements.size() > 0) {
424            System.out.println("replacement task is calculated to be: ");
425            new TaskTreeEncoder().encode(replacements.values().iterator().next().getTask(),
426                                         System.out);
427        }*/
428       
429        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
430        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
431
432        // create also replacements for potential parent iterations or optionals
433        IIteration harmonizedIteration = taskFactory.createNewIteration();
434        taskBuilder.setMarkedTask(harmonizedIteration, replacementTask);
435       
436        IOptional harmonizedOptional = taskFactory.createNewOptional();
437        taskBuilder.setMarkedTask(harmonizedOptional, replacementTask);
438       
439        for (IUserSession session : appData.getSessions()) {
440            replaceTaskInstances(session, replacements, similarTasks,
441                                 harmonizedIteration, harmonizedOptional);
442           
443            // several subsequent instances, which had formerly different tasks, may now have the
444            // same. Hence, they need to be merged. But as everything else would be way too complex,
445            // we only perform the merge, if they occur next to each other on the same level
446            mergeSubsequentIdenticalMarkingTemporalRelationships(session);
447        }
448       
449       
450        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
451        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
452        if (replacements.size() != 0) {
453            for (Map.Entry<ITaskInstance, ITaskInstance> entry : replacements.entrySet()) {
454                System.out.println
455                    ("did not replace instance " + entry.getKey() + " with " + entry.getValue());
456            }
457           
458            throw new RuntimeException();
459        }
460        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
461        // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
462    }
463
464    /**
465     *
466     */
467    private List<FlattenInstruction> getFlattenInstructions(SimilarTasks        similarTasks,
468                                                            RuleApplicationData appData)
469    {
470        TaskTraversal leftHandSideTraversal = similarTasks.getLeftHandSideTraversal();
471        TaskTraversal rightHandSideTraversal = similarTasks.getRightHandSideTraversal();
472       
473        List<FlattenInstruction> result = new LinkedList<FlattenInstruction>();
474        TaskComparator comp = identTaskHandlStrat.getTaskComparator();
475       
476        // first create instructions for the deltas
477        for (Delta delta : similarTasks.getPatch().getDeltas()) {
478            if ((delta.getType() == Delta.TYPE.INSERT) ||
479                (delta.getType() == Delta.TYPE.DELETE))
480            {
481                // System.out.println("handling " + delta.getType());
482                Chunk chunk;
483                TaskPath insertAfterPath = null;
484                TaskPath insertBeforePath;
485               
486                if (delta.getType() == Delta.TYPE.INSERT) {
487                    chunk = delta.getRevised();
488                    int pos = delta.getOriginal().getPosition();
489                    // System.out.println(" position " + pos);
490                   
491                    if (pos > 0) {
492                        insertAfterPath = leftHandSideTraversal.getTraversalPaths()[pos - 1];
493                    }
494                    insertBeforePath = leftHandSideTraversal.getTraversalPaths()[pos];
495                }
496                else {
497                    chunk = delta.getOriginal();
498                    int pos = delta.getRevised().getPosition();
499                    if (pos > 0) {
500                        insertAfterPath = rightHandSideTraversal.getTraversalPaths()[pos - 1];
501                    }
502                    insertBeforePath = rightHandSideTraversal.getTraversalPaths()[pos];
503                }
504               
505                ITask child = getTaskForChunk(chunk, appData);
506                IOptional optional;
507               
508                if (child instanceof IOptional) {
509                    optional = (IOptional) child;
510                }
511                else {
512                    optional = taskFactory.createNewOptional();
513                    taskBuilder.setMarkedTask(optional, child);
514                    // ((Task) optional).setDescription(optional.getDescription() +
515                    //                                  " created for " + delta.getType());
516                    optional = (IOptional) appData.ensureUnique(optional);
517                    createReplacementInstructions(chunk, optional, null, result);
518                }
519
520                // create a flatten instruction for the non-occurrence of the optional
521                result.add(new FlattenInstruction(insertAfterPath, insertBeforePath, optional));
522               
523            }
524            else if (delta.getType() == Delta.TYPE.CHANGE) {
525                ITask child1;
526                ITask child2;
527               
528                // check, if the change covers the whole traversal. If so reuse the root task.
529                // Otherwise, create intermediate tasks if required representing both sides
530                // of changes
531                if ((delta.getOriginal().getPosition() == 0) &&
532                    (delta.getOriginal().size() == similarTasks.getLeftHandSideTraversal().size()))
533                {
534                    child1 = similarTasks.getLeftHandSide();
535                }
536                else {
537                    child1 = getTaskForChunk(delta.getOriginal(), appData);
538                }
539               
540                if ((delta.getRevised().getPosition() == 0) &&
541                    (delta.getRevised().size() == similarTasks.getRightHandSideTraversal().size()))
542                {
543                    child2 = similarTasks.getRightHandSide();
544                }
545                else {
546                    child2 = getTaskForChunk(delta.getRevised(), appData);
547                }
548               
549                // check if either of the variants is an iteration or optional of the respective
550                // other variant. If so, ensure, that the iteration or optional are preserved
551                ITask markedTask1 = (child1 instanceof IMarkingTemporalRelationship) ?
552                    ((IMarkingTemporalRelationship) child1).getMarkedTask() : null;
553               
554                ITask markedTask2 = (child2 instanceof IMarkingTemporalRelationship) ?
555                    ((IMarkingTemporalRelationship) child2).getMarkedTask() : null;
556               
557                if (comp.equals(markedTask1, child2)) {
558                    if (child1 instanceof IOptional) {
559                        createReplacementInstructions
560                            (delta.getRevised(), (IOptional) child1, null, result);
561                    }
562                    else {
563                        throw new java.lang.UnsupportedOperationException("not implemented yet");
564                    }
565                }
566                else if (comp.equals(markedTask2, child1)) {
567                    if (child2 instanceof IOptional) {
568                        createReplacementInstructions
569                            (delta.getOriginal(), (IOptional) child2, null, result);
570                    }
571                    else {
572                        throw new java.lang.UnsupportedOperationException("not implemented yet");
573                    }
574                }
575                else if ((markedTask1 != null) && (comp.equals(markedTask1, markedTask2))) {
576                    throw new java.lang.UnsupportedOperationException("not implemented yet");
577                }
578                else {
579                    // its time to create a selection of both variants. If one is already
580                    // a selection, it is reused and extended, if required
581                    ITask expectedChild1 = null;
582                    ITask expectedChild2 = null;
583                   
584                    ISelection selection = null;
585                    if (child1 instanceof ISelection) {
586                        selection = (ISelection) child1;
587                    }
588               
589                    if (child2 instanceof ISelection) {
590                        if (selection == null) {
591                            // only child 2 is a selection --> extend it with child 1
592                            selection = (ISelection) child2;
593                            addSelectionChildIfRequired(selection, child1);
594                            expectedChild1 = child1;
595                        }
596                        else {
597                            // both are selections --> extend child 1 with variants of child2
598                            for (ITask child : ((ISelection) child2).getChildren()) {
599                                addSelectionChildIfRequired(selection, child);
600                            }
601                        }
602                    }
603                    else if (selection != null) {
604                        // only child 1 is a selection --> extend it with child 2
605                        addSelectionChildIfRequired(selection, child2);
606                        expectedChild2 = child2;
607                    }
608                    else if (selection == null) {
609                        // none of both is already a selection, so create a new one with both
610                        // of the children as variants
611                        selection = taskFactory.createNewSelection();
612                        // ((Task) selection).setDescription(selection.getDescription() +
613                        //                                   " created for " + delta.getType());
614                        taskBuilder.addChild(selection, child1);
615                        taskBuilder.addChild(selection, child2);
616                        expectedChild1 = child1;
617                        expectedChild2 = child2;
618                    }
619
620                    selection = (ISelection) appData.ensureUnique(selection);
621               
622                    createReplacementInstructions
623                        (delta.getOriginal(), selection, expectedChild1, result);
624                    createReplacementInstructions
625                        (delta.getRevised(), selection, expectedChild2, result);
626                }
627            }
628        }
629       
630        // create instructions to harmonize marking temporal relationships for untraversed tasks
631        int leftHandSideIndex = 0;
632        int rightHandSideIndex = 0;
633       
634        OUTER:
635        while (leftHandSideIndex < leftHandSideTraversal.getTraversalPaths().length) {
636            for (Delta delta : similarTasks.getPatch().getDeltas()) {
637                if (delta.getOriginal().getPosition() == leftHandSideIndex) {
638                    if (delta.getType() == Delta.TYPE.INSERT) {
639                        rightHandSideIndex += delta.getRevised().size();
640                        // do not continue OUTER to ensure, that the left hand side and the
641                        // right hand side coming directly after the insert are handled
642                    }
643                    else if (delta.getType() == Delta.TYPE.DELETE) {
644                        leftHandSideIndex += delta.getOriginal().size();
645                        continue OUTER;
646                    }
647                    else if (delta.getType() == Delta.TYPE.CHANGE) {
648                        leftHandSideIndex += delta.getOriginal().size();
649                        rightHandSideIndex += delta.getRevised().size();
650                        continue OUTER;
651                    }
652                }
653            }
654           
655            TaskPath leftPath = leftHandSideTraversal.getTraversalPaths()[leftHandSideIndex];
656            TaskPath rightPath = rightHandSideTraversal.getTraversalPaths()[rightHandSideIndex];
657           
658            if (comp.equals(leftPath.getLast(), rightPath.getLast())) {
659                if (leftPath.getTask(leftPath.size() - 2) instanceof IOptional) {
660                    IOptional optional = (IOptional) leftPath.getTask(leftPath.size() - 2);
661                    result.add
662                        (new FlattenInstruction(leftPath.subPath(0, leftPath.size()), optional));
663
664                    result.add(new FlattenInstruction(rightPath, optional));
665                }
666                else if (rightPath.getTask(rightPath.size() - 2) instanceof IOptional) {
667                    IOptional optional = (IOptional) rightPath.getTask(rightPath.size() - 2);
668                    result.add
669                        (new FlattenInstruction(rightPath.subPath(0, rightPath.size()), optional));
670
671                    result.add(new FlattenInstruction(leftPath, optional));
672                }
673            }
674           
675            leftHandSideIndex++;
676            rightHandSideIndex++;
677        }
678       
679       
680        // then create instructions for what stays the same
681        OUTER:
682        for (TaskPath path : leftHandSideTraversal.getTraversalPaths()) {
683            for (FlattenInstruction instruction : result) {
684                if (instruction.matches(path)) {
685                    continue OUTER;
686                }
687            }
688           
689            result.add(new FlattenInstruction(path));
690        }
691
692        OUTER:
693        for (TaskPath path : rightHandSideTraversal.getTraversalPaths()) {
694            for (FlattenInstruction instruction : result) {
695                if (instruction.matches(path)) {
696                    continue OUTER;
697                }
698            }
699           
700            result.add(new FlattenInstruction(path));
701        }
702
703        return result;
704    }
705
706    /**
707     *
708     */
709    private void addSelectionChildIfRequired(ISelection selection, ITask child) {
710        for (ITask candidate : selection.getChildren()) {
711            if (identTaskHandlStrat.getTaskComparator().equals(candidate, child)) {
712                return;
713            }
714        }
715       
716        taskBuilder.addChild(selection, child);
717    }
718
719    /**
720     *
721     */
722    private ITask getTaskForChunk(Chunk chunk, RuleApplicationData appData) {
723        if (chunk.size() == 1) {
724            TaskPath path = (TaskPath) chunk.getLines().get(0);
725            return path.getTask(path.size() - 1);
726        }
727        else {
728            ISequence task = taskFactory.createNewSequence();
729            // ((Task) task).setDescription(task.getDescription() +
730            //                              " created to represent a chunk");
731
732            for (Object pathObj : chunk.getLines()) {
733                TaskPath path = (TaskPath) pathObj;
734                taskBuilder.addChild(task, path.getLast());
735            }
736           
737            return appData.ensureUnique(task);
738        }
739    }
740
741    /**
742     *
743     */
744    private void createReplacementInstructions(Chunk                    chunk,
745                                               ITask                    replacement,
746                                               ITask                    selectedChild,
747                                               List<FlattenInstruction> result)
748    {
749        // create a flatten instruction for the occurrence of the replacement
750        for (Object pathObj : chunk.getLines()) {
751            TaskPath path = (TaskPath) pathObj;
752           
753            if (replacement instanceof IOptional) {
754                result.add(new FlattenInstruction(path, (IOptional) replacement));
755            }
756            else if (replacement instanceof ISelection) {
757                result.add
758                    (new FlattenInstruction(path, (ISelection) replacement, selectedChild));
759            }
760        }
761    }
762
763    /**
764     *
765     */
766    private void flattenInstances(ITask                            task,
767                                  List<FlattenInstruction>         flattenInstructions,
768                                  Map<ITaskInstance, IUserSession> flattenedSessions,
769                                  List<TaskPath>                   excludes)
770    {
771        //int i = 0;
772        for (ITaskInstance instance : task.getInstances()) {
773            /*System.out.println("flattening instance " + i++ + " of task " + task);
774            new TaskTreeEncoder().encode(instance, System.out, excludes);*/
775           
776            TaskPath taskPath = new TaskPath();
777            taskPath.add(instance.getTask(), 0);
778            IUserSession session = taskFactory.createUserSession();
779            flattenInstance(instance, taskPath, flattenInstructions, session,
780                            new LinkedList<TaskPath>());
781           
782            //new TaskTreeEncoder().encode(session, System.out, excludes);
783           
784            flattenedSessions.put(instance, session);
785        }
786    }
787
788    /**
789     *
790     */
791    private void flattenInstance(ITaskInstance            instance,
792                                 TaskPath                 taskPath,
793                                 List<FlattenInstruction> flattenInstructions,
794                                 IUserSession             session,
795                                 List<TaskPath>           previousPaths)
796    {
797        boolean instructionApplied = false;
798       
799        TaskComparator comp = identTaskHandlStrat.getTaskComparator();
800       
801        //System.out.println("applying instructions on " + taskPath);
802       
803        for (FlattenInstruction instruction : flattenInstructions) {
804            if (instruction.matches(taskPath)) {
805                //System.out.print("found instruction ");
806                //instruction.dump(System.out);
807               
808                switch (instruction.getInstruction()) {
809                    case DONT_TRAVERSE: {
810                        if (instance != null) {
811                            taskBuilder.addTaskInstance(session, instance);
812                        }
813                        instructionApplied = true;
814                        break;
815                    }
816                    case MAKE_OPTIONAL: {
817                        instructionApplied = true;
818
819                        if (instance == null) {
820                            break;
821                        }
822                       
823                        IOptional optional = instruction.getOptional();
824
825                        if (comp.equals(optional, instance.getTask())) {
826                            // the instance is already an instance of the correct optional --> reuse
827                            taskBuilder.addTaskInstance(session, instance);
828                        }
829                        else if (comp.equals(optional.getMarkedTask(), instance.getTask())) {
830                            IOptionalInstance optionalInstance =
831                                taskFactory.createNewTaskInstance(optional);
832                            taskBuilder.setChild(optionalInstance, instance);
833                            taskBuilder.addTaskInstance(session, optionalInstance);
834                        }
835                        else if (optional.getMarkedTask() instanceof ISequence) {
836                            // first check, if the instance was already created, if not create it
837                            ISequenceInstance sequenceInstance = ensureSequenceChildInstanceFor
838                                (optional, (ISequence) optional.getMarkedTask(), session);
839
840                            taskBuilder.addChild(sequenceInstance, instance);
841                        }
842                        break;
843                    }
844                    case MAKE_SELECTION: {
845                        instructionApplied = true;
846
847                        if (instance == null) {
848                            break;
849                        }
850                       
851                        ISelection selection = instruction.getSelection();
852
853                        if (comp.equals(instruction.getSelectedChild(), instance.getTask())) {
854                            ISelectionInstance selectionInstance =
855                                taskFactory.createNewTaskInstance(selection);
856                            taskBuilder.setChild(selectionInstance, instance);
857                            taskBuilder.addTaskInstance(session, selectionInstance);
858                        }
859                        else if (instruction.getSelectedChild() == null) {
860                            // both variants were already selections. They will have been merged.
861                            // Hence we can reuse the child instances of the existing selection
862                            // instances.
863                            if (comp.equals(instance.getTask(), selection)) {
864                                taskBuilder.addTaskInstance(session, instance);
865                            }
866                            else {
867                                ISelectionInstance selectionInstance =
868                                    taskFactory.createNewTaskInstance(selection);
869                                taskBuilder.setChild(selectionInstance,
870                                                     ((ISelectionInstance) instance).getChild());
871                                taskBuilder.addTaskInstance(session, selectionInstance);
872                               
873                                taskBuilder.discardTaskInstance(instance);
874                            }
875                        }
876                        else if (instruction.getSelectedChild() instanceof ISequence) {
877                            // first check, if the instance was already created, if not create it
878                            ISequenceInstance sequenceInstance = ensureSequenceChildInstanceFor
879                                (selection, (ISequence) instruction.getSelectedChild(),  session);
880
881                            taskBuilder.addChild(sequenceInstance, instance);
882                        }
883
884                        break;
885                    }
886                    case INTEGRATE_OPTIONAL: {
887                        TaskPath previousPath = previousPaths.size() > 0 ?
888                            previousPaths.get(previousPaths.size() - 1) : null;
889                       
890                        if (pathsMatch(instruction.getPrecedingPath(), previousPath)) {
891                            IOptional optional = instruction.getOptional();
892                            IOptionalInstance optionalInstance =
893                                    taskFactory.createNewTaskInstance(optional);
894                            taskBuilder.addTaskInstance(session, optionalInstance);
895                        }
896                       
897                        if (instance != null) {
898                            taskBuilder.addTaskInstance(session, instance);
899                        }
900                       
901                        instructionApplied = true;
902                        break;
903                    }
904                    default : {
905                        throw new RuntimeException("unknown instruction type");
906                    }
907                }
908            }
909           
910            if (instructionApplied) {
911                break;
912            }
913        }
914       
915        if (!instructionApplied) {
916            ITask task = taskPath.getLast();
917            if (task instanceof IIteration) {
918                taskPath.add(((IIteration) task).getMarkedTask(), 0);
919               
920                if (instance != null) {
921                    for (ITaskInstance child : (IIterationInstance) instance) {
922                        flattenInstance
923                            (child, taskPath, flattenInstructions, session, previousPaths);
924                    }
925                }
926                else {
927                    flattenInstance(null, taskPath, flattenInstructions, session, previousPaths);
928                }
929               
930                taskPath.removeLast();
931            }
932            else if (task instanceof ISequence) {
933                List<ITask> children = ((ISequence) task).getChildren();
934                for (int i = 0; i < children.size(); i++) {
935                    ITaskInstance child = null;
936                    if (instance != null) {
937                        child = ((ISequenceInstance) instance).get(i);
938                    }
939                    taskPath.add(children.get(i), i);
940                    flattenInstance
941                        (child, taskPath, flattenInstructions, session, previousPaths);
942                    taskPath.removeLast();
943                }
944            }
945            else if (task instanceof ISelection) {
946                if (instance != null) {
947                    taskPath.add(((ISelectionInstance) instance).getChild().getTask(), 0);
948                    flattenInstance(((ISelectionInstance) instance).getChild(), taskPath,
949                                    flattenInstructions, session, previousPaths);
950                    taskPath.removeLast();
951                }
952                else {
953                    // check if the selection has any child for which a rule must be considered
954                    List<ITask> children = ((ISelection) task).getChildren();
955                    for (int i = 0; i < children.size(); i++) {
956                        taskPath.add(children.get(i), i);
957                        flattenInstance
958                            (null, taskPath, flattenInstructions, session, previousPaths);
959                        taskPath.removeLast();
960                    }
961                }
962            }
963            else if (task instanceof IOptional) {
964                taskPath.add(((IOptional) task).getMarkedTask(), 0);
965                ITaskInstance child = null;
966                if (instance != null) {
967                    child = ((IOptionalInstance) instance).getChild();
968                }
969                flattenInstance(child, taskPath, flattenInstructions, session, previousPaths);
970                taskPath.removeLast();
971               
972                if ((instance != null) && (child == null)) {
973                    // add the empty optional instance
974                    taskBuilder.addTaskInstance(session, instance);
975                    previousPaths.add(new TaskPath(taskPath));
976                }
977            }
978            else if (instance != null) {
979                taskBuilder.addTaskInstance(session, instance);
980                previousPaths.add(new TaskPath(taskPath));
981            }
982        }
983        else {
984            previousPaths.add(new TaskPath(taskPath));
985        }
986    }
987
988    /**
989     *
990     */
991    private ISequenceInstance ensureSequenceChildInstanceFor(ITask        replacement,
992                                                             ISequence    expectedChildSequence,
993                                                             IUserSession session)
994    {
995        // first check, if there is already an instance of the expected sequence. If so, check if
996        // its child is a sequence instance. If not create a new task instance with an appropriate
997        // sequence instance
998        ITaskInstance prevInstance =
999            session.size() > 0 ? session.get(session.size() - 1) : null;
1000       
1001        ISequenceInstance sequenceInstance = null;
1002       
1003        if ((prevInstance != null) &&
1004            (identTaskHandlStrat.getTaskComparator().equals(prevInstance.getTask(), replacement)))
1005        {
1006            if ((prevInstance instanceof IOptionalInstance) &&
1007                (((IOptionalInstance) prevInstance).getChild() instanceof ISequenceInstance))
1008            {
1009                sequenceInstance =
1010                    (ISequenceInstance) ((IOptionalInstance) prevInstance).getChild();
1011            }
1012            else if ((prevInstance instanceof ISelectionInstance) &&
1013                    (((ISelectionInstance) prevInstance).getChild() instanceof ISequenceInstance))
1014            {
1015                sequenceInstance =
1016                    (ISequenceInstance) ((ISelectionInstance) prevInstance).getChild();
1017            }
1018        }
1019       
1020        // although we found a sequence instance as expected, this instance might already be fully
1021        // created and a new (iterated) one must be created. Check for it and handle correctly
1022        if ((sequenceInstance != null) &&
1023            (sequenceInstance.size() == expectedChildSequence.getChildren().size()))
1024        {
1025            sequenceInstance = null;
1026        }
1027       
1028        if (sequenceInstance == null) {
1029            //System.out.println("creating new sequence instance of selected child");
1030            sequenceInstance = taskFactory.createNewTaskInstance(expectedChildSequence);
1031               
1032            ITaskInstance replacementInstance = null;
1033           
1034            if (replacement instanceof IOptional) {
1035                replacementInstance = taskFactory.createNewTaskInstance((IOptional) replacement);
1036                taskBuilder.setChild((IOptionalInstance) replacementInstance, sequenceInstance);
1037            }
1038            else if (replacement instanceof ISelection) {
1039               
1040                /*System.out.println("replacement: ");
1041                new TaskTreeEncoder().encode(replacement, System.out);
1042               
1043                System.out.println("expectedChild: ");
1044                new TaskTreeEncoder().encode(expectedChildSequence, System.out);*/
1045               
1046                replacementInstance = taskFactory.createNewTaskInstance((ISelection) replacement);
1047                taskBuilder.setChild((ISelectionInstance) replacementInstance, sequenceInstance);
1048            }
1049           
1050            taskBuilder.addTaskInstance(session, replacementInstance);
1051        }
1052       
1053        return sequenceInstance;
1054    }
1055
1056    /**
1057     *
1058     */
1059    private void replaceTaskInstances(ITaskInstanceList                 taskInstanceList,
1060                                      Map<ITaskInstance, ITaskInstance> replacements,
1061                                      SimilarTasks                      similarTasks,
1062                                      IIteration                        harmonizedIteration,
1063                                      IOptional                         harmonizedOptional)
1064    {
1065        for (int i = 0; i < taskInstanceList.size(); i++) {
1066            ITaskInstance childInstance = taskInstanceList.get(i);
1067            ITaskInstance replacement = replacements.remove(childInstance);
1068           
1069            if (replacement != null) {
1070               
1071                // update the model for sequences (others are updated in the calling method)
1072                if (taskInstanceList instanceof ISequenceInstance) {
1073                    ISequence task = ((ISequenceInstance) taskInstanceList).getSequence();
1074                    taskBuilder.setChild(task, i, replacement.getTask());
1075                }
1076               
1077                // perform the actual replacement and throw away the instance
1078                taskBuilder.setTaskInstance(taskInstanceList, i, replacement);
1079                TaskPath path = new TaskPath();
1080                path.add(childInstance.getTask(), 0);
1081                discardTaskInstancesNotBelongingToTraversals(childInstance, path, similarTasks);
1082            }
1083            else {
1084                ITask modelUpdate = replaceTaskInstances(childInstance, replacements, similarTasks,
1085                                                         harmonizedIteration, harmonizedOptional);
1086               
1087                if (modelUpdate != null) {
1088                    if (taskInstanceList instanceof ISequenceInstance) {
1089                        taskBuilder.setChild
1090                            (((ISequenceInstance) taskInstanceList).getSequence(), i, modelUpdate);
1091                    }
1092                }
1093            }
1094        }
1095    }
1096
1097    /**
1098     *
1099     */
1100    private void replaceTaskInstances(IOptionalInstance                 optionalInstance,
1101                                      Map<ITaskInstance, ITaskInstance> replacements,
1102                                      SimilarTasks                      similarTasks,
1103                                      IIteration                        harmonizedIteration,
1104                                      IOptional                         harmonizedOptional)
1105    {
1106        ITaskInstance childInstance = optionalInstance.getChild();
1107       
1108        if (childInstance != null) {
1109            ITaskInstance replacement = replacements.remove(childInstance);
1110           
1111            if (replacement != null) {
1112                // do not update the model --> is updated in the calling method
1113               
1114                // perform the actual replacement and throw away the instance
1115                taskBuilder.setChild(optionalInstance, replacement);
1116                TaskPath path = new TaskPath();
1117                path.add(childInstance.getTask(), 0);
1118                discardTaskInstancesNotBelongingToTraversals(childInstance, path, similarTasks);
1119            }
1120            else {
1121                ITask modelUpdate = replaceTaskInstances(childInstance, replacements, similarTasks,
1122                                                         harmonizedIteration, harmonizedOptional);
1123               
1124                if (modelUpdate != null) {
1125                    taskBuilder.setMarkedTask(optionalInstance.getOptional(), modelUpdate);
1126                }
1127            }
1128        }
1129    }
1130
1131    /**
1132     *
1133     */
1134    private void replaceTaskInstances(ISelectionInstance                selectionInstance,
1135                                      Map<ITaskInstance, ITaskInstance> replacements,
1136                                      SimilarTasks                      similarTasks,
1137                                      IIteration                        harmonizedIteration,
1138                                      IOptional                         harmonizedOptional)
1139    {
1140        TaskComparator comparator = identTaskHandlStrat.getTaskComparator();
1141       
1142        ITaskInstance childInstance = selectionInstance.getChild();
1143       
1144        if (childInstance != null) {
1145            ITaskInstance replacement = replacements.remove(childInstance);
1146           
1147            if (replacement != null) {
1148               
1149                if (replacement instanceof ISelectionInstance) {
1150                    taskBuilder.discardTaskInstance(replacement);
1151                    replacement = ((ISelectionInstance) replacement).getChild();
1152                }
1153               
1154                // update the model
1155                // we replace all instances of the merged tasks with instances of a new task.
1156                // hence, we also have to remove the task of the replaced children from the
1157                // available variants of the selection
1158                taskBuilder.removeChild(selectionInstance.getSelection(), childInstance.getTask());
1159
1160                boolean found = false;
1161                for (ITask child : selectionInstance.getSelection().getChildren()) {
1162                    if (comparator.equals(child, replacement.getTask())) {
1163                        found = true;
1164                        break;
1165                    }
1166                }
1167               
1168                if (!found) {
1169                    taskBuilder.addChild(selectionInstance.getSelection(), replacement.getTask());
1170                }
1171               
1172                // perform the actual replacement and throw away the instance
1173                taskBuilder.setChild(selectionInstance, replacement);
1174                TaskPath path = new TaskPath();
1175                path.add(childInstance.getTask(), 0);
1176                discardTaskInstancesNotBelongingToTraversals(childInstance, path, similarTasks);
1177            }
1178            else {
1179                ITask modelUpdate = replaceTaskInstances(childInstance, replacements, similarTasks,
1180                                                         harmonizedIteration, harmonizedOptional);
1181               
1182                if (modelUpdate != null) {
1183                    taskBuilder.removeChild
1184                        (selectionInstance.getSelection(), childInstance.getTask());
1185
1186                    boolean found = false;
1187                    for (ITask child : selectionInstance.getSelection().getChildren()) {
1188                        if (comparator.equals(child, modelUpdate)) {
1189                            found = true;
1190                            break;
1191                        }
1192                    }
1193                   
1194                    if (!found) {
1195                        taskBuilder.addChild(selectionInstance.getSelection(), modelUpdate);
1196                    }
1197                }
1198            }
1199        }
1200    }
1201
1202    /**
1203     *
1204     */
1205    private ITask replaceTaskInstances(ITaskInstance                     childInstance,
1206                                       Map<ITaskInstance, ITaskInstance> replacements,
1207                                       SimilarTasks                      similarTasks,
1208                                       IIteration                        harmonizedIteration,
1209                                       IOptional                         harmonizedOptional)
1210    {
1211        ITask modelUpdate = null;
1212       
1213        if (childInstance instanceof IIterationInstance) {
1214            ITask markedTask = ((IIterationInstance) childInstance).getIteration().getMarkedTask();
1215           
1216            if ((markedTask == similarTasks.getLeftHandSide()) ||
1217                (markedTask == similarTasks.getRightHandSide()))
1218            {
1219                taskBuilder.setTask(childInstance, harmonizedIteration);
1220                modelUpdate = harmonizedIteration;
1221            }
1222
1223            replaceTaskInstances((ITaskInstanceList) childInstance, replacements, similarTasks,
1224                                 harmonizedIteration, harmonizedOptional);
1225        }
1226        else if (childInstance instanceof IOptionalInstance) {
1227            ITask markedTask = ((IOptionalInstance) childInstance).getOptional().getMarkedTask();
1228           
1229            if ((markedTask == similarTasks.getLeftHandSide()) ||
1230                (markedTask == similarTasks.getRightHandSide()))
1231            {
1232                taskBuilder.setTask(childInstance, harmonizedOptional);
1233                modelUpdate = harmonizedOptional;
1234            }
1235           
1236            replaceTaskInstances((IOptionalInstance) childInstance, replacements, similarTasks,
1237                                 harmonizedIteration, harmonizedOptional);
1238        }
1239        else if (childInstance instanceof ISelectionInstance) {
1240            replaceTaskInstances((ISelectionInstance) childInstance, replacements, similarTasks,
1241                                 harmonizedIteration, harmonizedOptional);
1242        }
1243        else if (childInstance instanceof ISequenceInstance) {
1244            replaceTaskInstances((ITaskInstanceList) childInstance, replacements, similarTasks,
1245                                 harmonizedIteration, harmonizedOptional);
1246        }
1247       
1248        return modelUpdate;
1249    }
1250
1251    /**
1252     *
1253     */
1254    private void discardTaskInstancesNotBelongingToTraversals(ITaskInstance instance,
1255                                                              TaskPath      pathToInstance,
1256                                                              SimilarTasks  similarTasks)
1257    {
1258        boolean discarded = false;
1259        for (TaskPath candidate : similarTasks.getLeftHandSideTraversal().getTraversalPaths()) {
1260            if (candidate.size() > pathToInstance.size()) {
1261                TaskPath potentialEqualSubPath = candidate.subPath(0, pathToInstance.size());
1262                if (pathsMatch(potentialEqualSubPath, pathToInstance)) {
1263                    taskBuilder.discardTaskInstance(instance);
1264                    discarded = true;
1265                    break;
1266                }
1267            }
1268        }
1269       
1270        if (!discarded) {
1271            for (TaskPath candidate : similarTasks.getRightHandSideTraversal().getTraversalPaths()) {
1272                if (candidate.size() > pathToInstance.size()) {
1273                    TaskPath potentialEqualSubPath = candidate.subPath(0, pathToInstance.size());
1274                    if (pathsMatch(potentialEqualSubPath, pathToInstance)) {
1275                        taskBuilder.discardTaskInstance(instance);
1276                        discarded = true;
1277                        break;
1278                    }
1279                }
1280            }
1281        }
1282       
1283        if (discarded) {
1284            // now also discard the children
1285            if (instance instanceof ISequenceInstance) {
1286                int index = 0;
1287                for (ITaskInstance childInstance : (ITaskInstanceList) instance) {
1288                    pathToInstance.add(childInstance.getTask(), index++);
1289                    discardTaskInstancesNotBelongingToTraversals
1290                        (childInstance, pathToInstance, similarTasks);
1291                    pathToInstance.removeLast();
1292                }
1293            }
1294            else if (instance instanceof IIterationInstance) {
1295                for (ITaskInstance childInstance : (ITaskInstanceList) instance) {
1296                    pathToInstance.add(childInstance.getTask(), 0);
1297                    discardTaskInstancesNotBelongingToTraversals
1298                        (childInstance, pathToInstance, similarTasks);
1299                    pathToInstance.removeLast();
1300                }
1301            }
1302            else if (instance instanceof IOptionalInstance) {
1303                ITaskInstance childInstance = ((IOptionalInstance) instance).getChild();
1304                if (childInstance != null) {
1305                    pathToInstance.add(childInstance.getTask(), 0);
1306                    discardTaskInstancesNotBelongingToTraversals
1307                        (childInstance, pathToInstance, similarTasks);
1308                    pathToInstance.removeLast();
1309                }
1310            }
1311            else if (instance instanceof ISelectionInstance) {
1312                ITaskInstance childInstance = ((ISelectionInstance) instance).getChild();
1313                pathToInstance.add(childInstance.getTask(), 0);
1314                discardTaskInstancesNotBelongingToTraversals
1315                    (childInstance, pathToInstance, similarTasks);
1316                pathToInstance.removeLast();
1317            }
1318        }
1319    }
1320   
1321    /**
1322     *
1323     */
1324    private void mergeSubsequentIdenticalMarkingTemporalRelationships(ITaskInstanceList list) {
1325        int index = 0;
1326        TaskComparator comparator = identTaskHandlStrat.getTaskComparator();
1327       
1328        while (index < (list.size() - 1)) {
1329            ITaskInstance instance1 = list.get(index);
1330            ITaskInstance instance2 = list.get(index + 1);
1331           
1332            if (comparator.equals(instance1.getTask(), instance2.getTask())) {
1333                if (instance1 instanceof IIterationInstance) {
1334                    // add the children of the second to the first iteration instance and discard
1335                    // the second
1336                    for (ITaskInstance child : (IIterationInstance) instance2) {
1337                        taskBuilder.addChild((IIterationInstance) instance1, child);
1338                    }
1339                   
1340                    taskBuilder.removeTaskInstance(list, index + 1);
1341                    taskBuilder.discardTaskInstance(instance2);
1342                }
1343                else if (instance1 instanceof IOptionalInstance) {
1344                    ITaskInstance optionalChildInstance1 =
1345                        ((IOptionalInstance) instance1).getChild();
1346                    ITaskInstance optionalChildInstance2 =
1347                            ((IOptionalInstance) instance2).getChild();
1348                       
1349                    if (optionalChildInstance1 == null) {
1350                        // independent of the second, just remove the first. The second will be the
1351                        // unique representation
1352                        taskBuilder.removeTaskInstance(list, index);
1353                    }
1354                    else if (optionalChildInstance2 == null) {
1355                        // remove the second. The first will be the unique representation
1356                        taskBuilder.removeTaskInstance(list, index + 1);
1357                    }
1358                    else if (optionalChildInstance1 instanceof IIterationInstance) {
1359                        // add all children of the second optional iteration instance to the
1360                        // first and discard the second
1361                        for (ITaskInstance child : (IIterationInstance) optionalChildInstance2) {
1362                            taskBuilder.addChild
1363                                ((IIterationInstance) optionalChildInstance1, child);
1364                        }
1365                       
1366                        taskBuilder.removeTaskInstance(list, index + 1);
1367                        taskBuilder.discardTaskInstance(instance2);
1368                    }
1369                    else {
1370                        // both optional children are no iterations --> create an iteration
1371                        // for them and add them both as children.
1372                        throw new java.lang.UnsupportedOperationException("not implemented yet");
1373                    }
1374                }
1375                else {
1376                    index++;
1377                }
1378            }
1379            else {
1380                index++;
1381            }
1382        }
1383       
1384        for (ITaskInstance child : list) {
1385            if (child instanceof ITaskInstanceList) {
1386                mergeSubsequentIdenticalMarkingTemporalRelationships((ITaskInstanceList) child);
1387            }
1388        }
1389    }
1390   
1391    /**
1392     *
1393     */
1394    private static boolean pathsMatch(TaskPath path1, TaskPath path2) {
1395        if (path1 == null) {
1396            return path2 == null;
1397        }
1398        else if ((path2 != null) && (path1.size() == path2.size())) {
1399            for (int i = 0; i < path1.size(); i++) {
1400                if (!path1.get(i).equals(path2.get(i))) {
1401                    return false;
1402                }
1403            }
1404            return true;
1405        }
1406        else {
1407            return false;
1408        }
1409    }
1410
1411    /**
1412     *
1413     */
1414    /*private boolean containsNewTask(ITask task, RuleApplicationData appData) {
1415        if (appData.isSelfCreatedTask(task)) {
1416            return true;
1417        }
1418        else if (task instanceof IStructuringTemporalRelationship) {
1419            for (ITask child : ((IStructuringTemporalRelationship) task).getChildren()) {
1420                if (containsNewTask(child, appData)) {
1421                    return true;
1422                }
1423            }
1424        }
1425        else if (task instanceof IMarkingTemporalRelationship) {
1426            return containsNewTask(((IMarkingTemporalRelationship) task).getMarkedTask(), appData);
1427        }
1428       
1429        return false;
1430    }*/
1431
1432    /**
1433     *
1434     */
1435    private class RuleApplicationData {
1436
1437        /**
1438         *
1439         */
1440        private List<IUserSession> sessions;
1441       
1442        /**
1443         *
1444         */
1445        private ITaskModel taskModel;
1446
1447        /**
1448         *
1449         */
1450        private MostSimilarTaskDeterminer mostSimilarTasksDeterminer =
1451            new MostSimilarTaskDeterminer(identTaskHandlStrat.getTaskComparator());
1452       
1453        /**
1454         *
1455         */
1456        private List<SimilarTasks> mostSimilarTasks;
1457       
1458        /**
1459         *
1460         */
1461        private List<ITask> newTasks = new LinkedList<ITask>();
1462       
1463        /**
1464         *
1465         */
1466        private RuleApplicationResult ruleApplicationResult = new RuleApplicationResult();
1467       
1468        /**
1469         *
1470         */
1471        private RuleApplicationData(List<IUserSession> sessions) {
1472            this.sessions = sessions;
1473        }
1474
1475        /**
1476         *
1477         */
1478        boolean isSelfCreatedTask(ITask task) {
1479            for (ITask candidate : newTasks) {
1480                if (candidate == task) {
1481                    return true;
1482                }
1483            }
1484           
1485            return false;
1486        }
1487
1488        /**
1489         * @return the mostSimilarTasksDeterminer
1490         */
1491        private MostSimilarTaskDeterminer getMostSimilarTasksDeterminer() {
1492            return mostSimilarTasksDeterminer;
1493        }
1494
1495        /**
1496         *
1497         */
1498        private void markAsAlreadyCondensed(ITask task1, ITask task2) {
1499            mostSimilarTasksDeterminer.addComparisonToSkip(task1, task2);
1500        }
1501
1502        /**
1503         *
1504         */
1505        private RuleApplicationResult finalizeRuleApplicationResult() {
1506            for (ITask newTask : newTasks) {
1507                ruleApplicationResult.addNewlyCreatedTask(newTask);
1508               
1509                if (newTask instanceof IOptional) {
1510                    if (isSelfCreatedTask(((IOptional) newTask).getMarkedTask()) &&
1511                        (((IOptional) newTask).getMarkedTask() instanceof ISequence))
1512                    {
1513                        ruleApplicationResult.addNewlyCreatedTask
1514                            (((IOptional) newTask).getMarkedTask());
1515                    }
1516                }
1517                else if (newTask instanceof ISelection) {
1518                    for (ITask child : ((ISelection) newTask).getChildren()) {
1519                        if (isSelfCreatedTask(child) && (child instanceof ISequence)) {
1520                            ruleApplicationResult.addNewlyCreatedTask(child);
1521                        }
1522                    }
1523                }
1524            }
1525           
1526           
1527            return ruleApplicationResult;
1528        }
1529
1530        /**
1531         * @return the tree
1532         */
1533        private List<IUserSession> getSessions() {
1534            return sessions;
1535        }
1536
1537        /**
1538         * @return the taskModel
1539         */
1540        private ITaskModel getTaskModel() {
1541            return taskModel;
1542        }
1543
1544        /**
1545         * @param taskModel the taskModel to set
1546         */
1547        private void setTaskModel(ITaskModel taskModel) {
1548            this.taskModel = taskModel;
1549        }
1550
1551        /**
1552         * @return the orderedDiffLevels
1553         */
1554        private List<SimilarTasks> getMostSimilarTasks() {
1555            return mostSimilarTasks;
1556        }
1557
1558        /**
1559         * @param orderedDiffLevels the orderedDiffLevels to set
1560         */
1561        private void setMostSimilarTasks(List<SimilarTasks> mostSimilarTasks) {
1562            this.mostSimilarTasks = mostSimilarTasks;
1563        }
1564       
1565        /**
1566         *
1567         */
1568        private ITask ensureUnique(ITask task) {
1569            // replacements done in this rule are either optionals or selections. So focus on them
1570            if (task instanceof IOptional) {
1571                for (ITask newTask : newTasks) {
1572                    if (newTask instanceof IOptional) {
1573                        ITask child1 = ((IOptional) task).getMarkedTask();
1574                        ITask child2 = ((IOptional) newTask).getMarkedTask();
1575                        if (createdChildEquals(child1, child2)) {
1576                            // System.out.println("reusing optional " + newTask + " for " + task);
1577                            return newTask;
1578                        }
1579                    }
1580                }
1581            }
1582            else if (task instanceof ISelection) {
1583                for (ITask newTask : newTasks) {
1584                    if (newTask instanceof ISelection) {
1585                        List<ITask> children1 = ((ISelection) task).getChildren();
1586                        List<ITask> children2 = ((ISelection) newTask).getChildren();
1587                        if (createdSelectionChildrenEqual(children1, children2)) {
1588                            /*System.out.println("reusing selection " + newTask + " for " + task);
1589                           
1590                            System.out.println("---------------------------- existing task");
1591                            new TaskTreeEncoder().encode(newTask, System.out);
1592                           
1593                            System.out.println("---------------------------- completely new task");
1594                            new TaskTreeEncoder().encode(task, System.out);*/
1595                           
1596                            return newTask;
1597                        }
1598                    }
1599                }
1600            }
1601            else if (task instanceof ISequence) {
1602                List<ISequence> allRelevant = new ArrayList<>();
1603               
1604                for (ITask candidate : newTasks) {
1605                    if (candidate instanceof ISequence) {
1606                        allRelevant.add((ISequence) candidate);
1607                    }
1608                }
1609               
1610                for (ITask candidate : taskModel.getTasks()) {
1611                    if (candidate instanceof ISequence) {
1612                        allRelevant.add((ISequence) candidate);
1613                    }
1614                }
1615               
1616                for (ISequence candidate : allRelevant) {
1617                    List<ITask> children1 = ((ISequence) task).getChildren();
1618                    List<ITask> children2 = ((ISequence) candidate).getChildren();
1619
1620                    boolean equal = false;
1621                    if (children1.size() == children2.size()) {
1622                        equal = true;
1623                        for (int i = 0; i < children1.size(); i++) {
1624                            if (children1.get(i) != children2.get(i)) {
1625                                equal = false;
1626                                break;
1627                            }
1628                        }
1629                    }
1630
1631                    if (equal) {
1632                        // System.out.println("reusing sequence " + candidate + " for " + task);
1633                        return candidate;
1634                    }
1635                }
1636            }
1637           
1638            newTasks.add(task);
1639           
1640            return task;
1641        }
1642
1643        /**
1644         *
1645         */
1646        private boolean createdSelectionChildrenEqual(List<ITask> children1, List<ITask> children2) {
1647            if (children1.size() != children2.size()) {
1648                return false;
1649            }
1650           
1651            for (ITask child1 : children1) {
1652                boolean found = false;
1653               
1654                for (ITask child2 : children2) {
1655                    if (createdChildEquals(child1, child2)) {
1656                        found = true;
1657                        break;
1658                    }
1659                }
1660               
1661                if (!found) {
1662                    return false;
1663                }
1664            }
1665           
1666            return true;
1667        }
1668
1669        /**
1670         *
1671         */
1672        private boolean createdChildEquals(ITask child1, ITask child2) {
1673            if (child1 == child2) {
1674                return true;
1675            }
1676            else if (!isSelfCreatedTask(child1) && !isSelfCreatedTask(child2)) {
1677                // the simple comparison can not work if the tasks are not self created
1678                return false;
1679            }
1680            else if ((child1 instanceof ISequence) && (child2 instanceof ISequence)) {
1681                ISequence sequence1 = (ISequence) child1;
1682                ISequence sequence2 = (ISequence) child2;
1683               
1684                if (sequence1.getChildren().size() != sequence2.getChildren().size()) {
1685                    return false;
1686                }
1687               
1688                for (int i = 0; i < sequence1.getChildren().size(); i++) {
1689                    if (sequence1.getChildren().get(i) != sequence2.getChildren().get(i)) {
1690                        return false;
1691                    }
1692                }
1693               
1694                return true;
1695            }
1696            else {
1697                return false;
1698            }
1699        }
1700    }
1701   
1702    /**
1703     *
1704     */
1705    private static class FlattenInstruction {
1706       
1707        /**
1708         *
1709         */
1710        private enum Instruction { DONT_TRAVERSE, MAKE_OPTIONAL, MAKE_SELECTION, INTEGRATE_OPTIONAL };
1711
1712        /** */
1713        private TaskPath path;
1714
1715        /** */
1716        private TaskPath precedingPath;
1717
1718        /** */
1719        private Instruction instruction;
1720
1721        /** */
1722        private IOptional optional = null;
1723
1724        /** */
1725        private ISelection selection = null;
1726
1727        /** */
1728        private ITask selectedChild = null;
1729
1730        /**
1731         *
1732         */
1733        private FlattenInstruction(TaskPath path) {
1734            this.path = path;
1735            this.instruction = Instruction.DONT_TRAVERSE;
1736        }
1737
1738        /**
1739         *
1740         */
1741        private FlattenInstruction(TaskPath path, IOptional optional) {
1742            this.path = path;
1743            this.instruction = Instruction.MAKE_OPTIONAL;
1744            this.optional = optional;
1745        }
1746
1747        /**
1748         *
1749         */
1750        private FlattenInstruction(TaskPath path, ISelection selection, ITask selectedChild) {
1751            this.path = path;
1752            this.instruction = Instruction.MAKE_SELECTION;
1753            this.selection = selection;
1754            this.selectedChild = selectedChild;
1755        }
1756
1757        /**
1758         *
1759         */
1760        private FlattenInstruction(TaskPath  precedingPath,
1761                                   TaskPath  path,
1762                                   IOptional optional)
1763        {
1764            this.path = path;
1765            this.precedingPath = precedingPath;
1766            this.instruction = Instruction.INTEGRATE_OPTIONAL;
1767            this.optional = optional;
1768        }
1769       
1770        /**
1771         *
1772         */
1773        IOptional getOptional() {
1774            return optional;
1775        }
1776
1777
1778        /**
1779         * @return the selection
1780         */
1781        ISelection getSelection() {
1782            return selection;
1783        }
1784
1785        /**
1786         * @return the selectedChild
1787         */
1788        ITask getSelectedChild() {
1789            return selectedChild;
1790        }
1791
1792        /**
1793         * @return the instruction
1794         */
1795        Instruction getInstruction() {
1796            return instruction;
1797        }
1798
1799        /**
1800         * @return the precedingPath
1801         */
1802        TaskPath getPrecedingPath() {
1803            return precedingPath;
1804        }
1805
1806        /**
1807         *
1808         */
1809        boolean matches(TaskPath path) {
1810            return pathsMatch(this.path, path);
1811        }
1812       
1813
1814//        /**
1815//         *
1816//         */
1817//        void dump(PrintStream out) {
1818//            for (int i = 0; i < path.size(); i++) {
1819//                out.print("-->");
1820//                out.print(path.getTask(i).getId());
1821//                out.print('(');
1822//                out.print(path.get(i).getIndex());
1823//                out.print(')');
1824//            }
1825//           
1826//            out.print("  ");
1827//            out.print(instruction);
1828//           
1829//            out.println();
1830//        }
1831       
1832    }
1833
1834}
Note: See TracBrowser for help on using the repository browser.