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

Last change on this file since 1967 was 1967, checked in by pharms, 9 years ago
  • added reusing selections being the only parents of a sequence that is to be merged
File size: 79.1 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                   
582                    ITask expectedChild1 = null;
583                    ITask expectedChild2 = null;
584                   
585                    ISelection selection = null;
586                    if (child1 instanceof ISelection) {
587                        selection = (ISelection) child1;
588                    }
589               
590                    if (child2 instanceof ISelection) {
591                        if (selection == null) {
592                            // only child 2 is a selection --> extend it with child 1
593                            selection = (ISelection) child2;
594                            addSelectionChildIfRequired(selection, child1);
595                            expectedChild1 = child1;
596                        }
597                        else {
598                            // both are selections --> extend child 1 with variants of child2
599                            for (ITask child : ((ISelection) child2).getChildren()) {
600                                addSelectionChildIfRequired(selection, child);
601                            }
602                        }
603                    }
604                    else if (selection != null) {
605                        // only child 1 is a selection --> extend it with child 2
606                        addSelectionChildIfRequired(selection, child2);
607                        expectedChild2 = child2;
608                    }
609                    else if (selection == null) {
610                        // it may also be the case, that one is a child of a selection and occurs
611                        // only as such and nowhere else. Then the parent selection is reused and
612                        // extended.
613                        for (ITask candidate : appData.taskModel.getTasks()) {
614                            if (candidate instanceof ISelection) {
615                                selection = (ISelection) candidate;
616                                ITask existingChildTask = null;
617                               
618                                if (selection.getChildren().contains(child1)) {
619                                    existingChildTask = child1;
620                                }
621                                else if (selection.getChildren().contains(child2)) {
622                                    existingChildTask = child2;
623                               }
624                               
625                                if (existingChildTask != null) {
626                                    int noOfInstances = 0;
627                                   
628                                    for (ITaskInstance instance : selection.getInstances()) {
629                                        ITaskInstance childInstance =
630                                            ((ISelectionInstance) instance).getChild();
631                                           
632                                        if (childInstance.getTask() == existingChildTask) {
633                                            noOfInstances++;
634                                        }
635                                    }
636                                   
637                                    if (noOfInstances < existingChildTask.getInstances().size()) {
638                                        // not all instances of the existing child task are covered
639                                        // by the parent selection. Hence, throw selection away and
640                                        // search for another one
641                                        selection = null;
642                                    }
643                                    else {
644                                        // found a parent selection --> reuse it but add the new
645                                        // variant
646                                        if (child1 == existingChildTask) {
647                                            addSelectionChildIfRequired(selection, child2);
648                                        }
649                                        else {
650                                            addSelectionChildIfRequired(selection, child1);
651                                        }
652                                        expectedChild1 = child1;
653                                        expectedChild2 = child2;
654                                        break;
655                                    }
656                                }
657                                else {
658                                    selection = null;
659                                }
660                            }
661                        }
662                       
663                        if (selection == null) {
664                            // none of both is already a selection, so create a new one with both
665                            // of the children as variants
666                            selection = taskFactory.createNewSelection();
667                            // ((Task) selection).setDescription(selection.getDescription() +
668                            //                                   " created for " + delta.getType());
669                            taskBuilder.addChild(selection, child1);
670                            taskBuilder.addChild(selection, child2);
671                            expectedChild1 = child1;
672                            expectedChild2 = child2;
673                        }
674                    }
675
676                    selection = (ISelection) appData.ensureUnique(selection);
677               
678                    createReplacementInstructions
679                        (delta.getOriginal(), selection, expectedChild1, result);
680                    createReplacementInstructions
681                        (delta.getRevised(), selection, expectedChild2, result);
682                }
683            }
684        }
685       
686        // create instructions to harmonize marking temporal relationships for untraversed tasks
687        int leftHandSideIndex = 0;
688        int rightHandSideIndex = 0;
689       
690        OUTER:
691        while (leftHandSideIndex < leftHandSideTraversal.getTraversalPaths().length) {
692            for (Delta delta : similarTasks.getPatch().getDeltas()) {
693                if (delta.getOriginal().getPosition() == leftHandSideIndex) {
694                    if (delta.getType() == Delta.TYPE.INSERT) {
695                        rightHandSideIndex += delta.getRevised().size();
696                        // do not continue OUTER to ensure, that the left hand side and the
697                        // right hand side coming directly after the insert are handled
698                    }
699                    else if (delta.getType() == Delta.TYPE.DELETE) {
700                        leftHandSideIndex += delta.getOriginal().size();
701                        continue OUTER;
702                    }
703                    else if (delta.getType() == Delta.TYPE.CHANGE) {
704                        leftHandSideIndex += delta.getOriginal().size();
705                        rightHandSideIndex += delta.getRevised().size();
706                        continue OUTER;
707                    }
708                }
709            }
710           
711            TaskPath leftPath = leftHandSideTraversal.getTraversalPaths()[leftHandSideIndex];
712            TaskPath rightPath = rightHandSideTraversal.getTraversalPaths()[rightHandSideIndex];
713           
714            if (comp.equals(leftPath.getLast(), rightPath.getLast())) {
715                if (leftPath.getTask(leftPath.size() - 2) instanceof IOptional) {
716                    IOptional optional = (IOptional) leftPath.getTask(leftPath.size() - 2);
717                    result.add
718                        (new FlattenInstruction(leftPath.subPath(0, leftPath.size()), optional));
719
720                    result.add(new FlattenInstruction(rightPath, optional));
721                }
722                else if (rightPath.getTask(rightPath.size() - 2) instanceof IOptional) {
723                    IOptional optional = (IOptional) rightPath.getTask(rightPath.size() - 2);
724                    result.add
725                        (new FlattenInstruction(rightPath.subPath(0, rightPath.size()), optional));
726
727                    result.add(new FlattenInstruction(leftPath, optional));
728                }
729            }
730           
731            leftHandSideIndex++;
732            rightHandSideIndex++;
733        }
734       
735       
736        // then create instructions for what stays the same
737        OUTER:
738        for (TaskPath path : leftHandSideTraversal.getTraversalPaths()) {
739            for (FlattenInstruction instruction : result) {
740                if (instruction.matches(path)) {
741                    continue OUTER;
742                }
743            }
744           
745            result.add(new FlattenInstruction(path));
746        }
747
748        OUTER:
749        for (TaskPath path : rightHandSideTraversal.getTraversalPaths()) {
750            for (FlattenInstruction instruction : result) {
751                if (instruction.matches(path)) {
752                    continue OUTER;
753                }
754            }
755           
756            result.add(new FlattenInstruction(path));
757        }
758
759        return result;
760    }
761
762    /**
763     *
764     */
765    private void addSelectionChildIfRequired(ISelection selection, ITask child) {
766        for (ITask candidate : selection.getChildren()) {
767            if (identTaskHandlStrat.getTaskComparator().equals(candidate, child)) {
768                return;
769            }
770        }
771       
772        taskBuilder.addChild(selection, child);
773    }
774
775    /**
776     *
777     */
778    private ITask getTaskForChunk(Chunk chunk, RuleApplicationData appData) {
779        if (chunk.size() == 1) {
780            TaskPath path = (TaskPath) chunk.getLines().get(0);
781            return path.getTask(path.size() - 1);
782        }
783        else {
784            ISequence task = taskFactory.createNewSequence();
785            // ((Task) task).setDescription(task.getDescription() +
786            //                              " created to represent a chunk");
787
788            for (Object pathObj : chunk.getLines()) {
789                TaskPath path = (TaskPath) pathObj;
790                taskBuilder.addChild(task, path.getLast());
791            }
792           
793            return appData.ensureUnique(task);
794        }
795    }
796
797    /**
798     *
799     */
800    private void createReplacementInstructions(Chunk                    chunk,
801                                               ITask                    replacement,
802                                               ITask                    selectedChild,
803                                               List<FlattenInstruction> result)
804    {
805        // create a flatten instruction for the occurrence of the replacement
806        for (Object pathObj : chunk.getLines()) {
807            TaskPath path = (TaskPath) pathObj;
808           
809            if (replacement instanceof IOptional) {
810                result.add(new FlattenInstruction(path, (IOptional) replacement));
811            }
812            else if (replacement instanceof ISelection) {
813                result.add
814                    (new FlattenInstruction(path, (ISelection) replacement, selectedChild));
815            }
816        }
817    }
818
819    /**
820     *
821     */
822    private void flattenInstances(ITask                            task,
823                                  List<FlattenInstruction>         flattenInstructions,
824                                  Map<ITaskInstance, IUserSession> flattenedSessions,
825                                  List<TaskPath>                   excludes)
826    {
827        //int i = 0;
828        for (ITaskInstance instance : task.getInstances()) {
829            /*System.out.println("flattening instance " + i++ + " of task " + task);
830            new TaskTreeEncoder().encode(instance, System.out, excludes);*/
831           
832            TaskPath taskPath = new TaskPath();
833            taskPath.add(instance.getTask(), 0);
834            IUserSession session = taskFactory.createUserSession();
835            flattenInstance(instance, taskPath, flattenInstructions, session,
836                            new LinkedList<TaskPath>());
837           
838            //new TaskTreeEncoder().encode(session, System.out, excludes);
839           
840            flattenedSessions.put(instance, session);
841        }
842    }
843
844    /**
845     *
846     */
847    private void flattenInstance(ITaskInstance            instance,
848                                 TaskPath                 taskPath,
849                                 List<FlattenInstruction> flattenInstructions,
850                                 IUserSession             session,
851                                 List<TaskPath>           previousPaths)
852    {
853        boolean instructionApplied = false;
854       
855        TaskComparator comp = identTaskHandlStrat.getTaskComparator();
856       
857        //System.out.println("applying instructions on " + taskPath);
858       
859        for (FlattenInstruction instruction : flattenInstructions) {
860            if (instruction.matches(taskPath)) {
861                //System.out.print("found instruction ");
862                //instruction.dump(System.out);
863               
864                switch (instruction.getInstruction()) {
865                    case DONT_TRAVERSE: {
866                        if (instance != null) {
867                            taskBuilder.addTaskInstance(session, instance);
868                        }
869                        instructionApplied = true;
870                        break;
871                    }
872                    case MAKE_OPTIONAL: {
873                        instructionApplied = true;
874
875                        if (instance == null) {
876                            break;
877                        }
878                       
879                        IOptional optional = instruction.getOptional();
880
881                        if (comp.equals(optional, instance.getTask())) {
882                            // the instance is already an instance of the correct optional --> reuse
883                            taskBuilder.addTaskInstance(session, instance);
884                        }
885                        else if (comp.equals(optional.getMarkedTask(), instance.getTask())) {
886                            IOptionalInstance optionalInstance =
887                                taskFactory.createNewTaskInstance(optional);
888                            taskBuilder.setChild(optionalInstance, instance);
889                            taskBuilder.addTaskInstance(session, optionalInstance);
890                        }
891                        else if (optional.getMarkedTask() instanceof ISequence) {
892                            // first check, if the instance was already created, if not create it
893                            ISequenceInstance sequenceInstance = ensureSequenceChildInstanceFor
894                                (optional, (ISequence) optional.getMarkedTask(), session);
895
896                            taskBuilder.addChild(sequenceInstance, instance);
897                        }
898                        break;
899                    }
900                    case MAKE_SELECTION: {
901                        instructionApplied = true;
902
903                        if (instance == null) {
904                            break;
905                        }
906                       
907                        ISelection selection = instruction.getSelection();
908
909                        if (comp.equals(instruction.getSelectedChild(), instance.getTask())) {
910                            ISelectionInstance selectionInstance =
911                                taskFactory.createNewTaskInstance(selection);
912                            taskBuilder.setChild(selectionInstance, instance);
913                            taskBuilder.addTaskInstance(session, selectionInstance);
914                        }
915                        else if (instruction.getSelectedChild() == null) {
916                            // both variants were already selections. They will have been merged.
917                            // Hence we can reuse the child instances of the existing selection
918                            // instances.
919                            if (comp.equals(instance.getTask(), selection)) {
920                                taskBuilder.addTaskInstance(session, instance);
921                            }
922                            else {
923                                ISelectionInstance selectionInstance =
924                                    taskFactory.createNewTaskInstance(selection);
925                                taskBuilder.setChild(selectionInstance,
926                                                     ((ISelectionInstance) instance).getChild());
927                                taskBuilder.addTaskInstance(session, selectionInstance);
928                               
929                                taskBuilder.discardTaskInstance(instance);
930                            }
931                        }
932                        else if (instruction.getSelectedChild() instanceof ISequence) {
933                            // first check, if the instance was already created, if not create it
934                            ISequenceInstance sequenceInstance = ensureSequenceChildInstanceFor
935                                (selection, (ISequence) instruction.getSelectedChild(),  session);
936
937                            taskBuilder.addChild(sequenceInstance, instance);
938                        }
939
940                        break;
941                    }
942                    case INTEGRATE_OPTIONAL: {
943                        TaskPath previousPath = previousPaths.size() > 0 ?
944                            previousPaths.get(previousPaths.size() - 1) : null;
945                       
946                        if (pathsMatch(instruction.getPrecedingPath(), previousPath)) {
947                            IOptional optional = instruction.getOptional();
948                            IOptionalInstance optionalInstance =
949                                    taskFactory.createNewTaskInstance(optional);
950                            taskBuilder.addTaskInstance(session, optionalInstance);
951                        }
952                       
953                        if (instance != null) {
954                            taskBuilder.addTaskInstance(session, instance);
955                        }
956                       
957                        instructionApplied = true;
958                        break;
959                    }
960                    default : {
961                        throw new RuntimeException("unknown instruction type");
962                    }
963                }
964            }
965           
966            if (instructionApplied) {
967                break;
968            }
969        }
970       
971        if (!instructionApplied) {
972            ITask task = taskPath.getLast();
973            if (task instanceof IIteration) {
974                taskPath.add(((IIteration) task).getMarkedTask(), 0);
975               
976                if (instance != null) {
977                    for (ITaskInstance child : (IIterationInstance) instance) {
978                        flattenInstance
979                            (child, taskPath, flattenInstructions, session, previousPaths);
980                    }
981                }
982                else {
983                    flattenInstance(null, taskPath, flattenInstructions, session, previousPaths);
984                }
985               
986                taskPath.removeLast();
987            }
988            else if (task instanceof ISequence) {
989                List<ITask> children = ((ISequence) task).getChildren();
990                for (int i = 0; i < children.size(); i++) {
991                    ITaskInstance child = null;
992                    if (instance != null) {
993                        child = ((ISequenceInstance) instance).get(i);
994                    }
995                    taskPath.add(children.get(i), i);
996                    flattenInstance
997                        (child, taskPath, flattenInstructions, session, previousPaths);
998                    taskPath.removeLast();
999                }
1000            }
1001            else if (task instanceof ISelection) {
1002                if (instance != null) {
1003                    taskPath.add(((ISelectionInstance) instance).getChild().getTask(), 0);
1004                    flattenInstance(((ISelectionInstance) instance).getChild(), taskPath,
1005                                    flattenInstructions, session, previousPaths);
1006                    taskPath.removeLast();
1007                }
1008                else {
1009                    // check if the selection has any child for which a rule must be considered
1010                    List<ITask> children = ((ISelection) task).getChildren();
1011                    for (int i = 0; i < children.size(); i++) {
1012                        taskPath.add(children.get(i), i);
1013                        flattenInstance
1014                            (null, taskPath, flattenInstructions, session, previousPaths);
1015                        taskPath.removeLast();
1016                    }
1017                }
1018            }
1019            else if (task instanceof IOptional) {
1020                taskPath.add(((IOptional) task).getMarkedTask(), 0);
1021                ITaskInstance child = null;
1022                if (instance != null) {
1023                    child = ((IOptionalInstance) instance).getChild();
1024                }
1025                flattenInstance(child, taskPath, flattenInstructions, session, previousPaths);
1026                taskPath.removeLast();
1027               
1028                if ((instance != null) && (child == null)) {
1029                    // add the empty optional instance
1030                    taskBuilder.addTaskInstance(session, instance);
1031                    previousPaths.add(new TaskPath(taskPath));
1032                }
1033            }
1034            else if (instance != null) {
1035                taskBuilder.addTaskInstance(session, instance);
1036                previousPaths.add(new TaskPath(taskPath));
1037            }
1038        }
1039        else {
1040            previousPaths.add(new TaskPath(taskPath));
1041        }
1042    }
1043
1044    /**
1045     *
1046     */
1047    private ISequenceInstance ensureSequenceChildInstanceFor(ITask        replacement,
1048                                                             ISequence    expectedChildSequence,
1049                                                             IUserSession session)
1050    {
1051        // first check, if there is already an instance of the expected sequence. If so, check if
1052        // its child is a sequence instance. If not create a new task instance with an appropriate
1053        // sequence instance
1054        ITaskInstance prevInstance =
1055            session.size() > 0 ? session.get(session.size() - 1) : null;
1056       
1057        ISequenceInstance sequenceInstance = null;
1058       
1059        if ((prevInstance != null) &&
1060            (identTaskHandlStrat.getTaskComparator().equals(prevInstance.getTask(), replacement)))
1061        {
1062            if ((prevInstance instanceof IOptionalInstance) &&
1063                (((IOptionalInstance) prevInstance).getChild() instanceof ISequenceInstance))
1064            {
1065                sequenceInstance =
1066                    (ISequenceInstance) ((IOptionalInstance) prevInstance).getChild();
1067            }
1068            else if ((prevInstance instanceof ISelectionInstance) &&
1069                    (((ISelectionInstance) prevInstance).getChild() instanceof ISequenceInstance))
1070            {
1071                sequenceInstance =
1072                    (ISequenceInstance) ((ISelectionInstance) prevInstance).getChild();
1073            }
1074        }
1075       
1076        // although we found a sequence instance as expected, this instance might already be fully
1077        // created and a new (iterated) one must be created. Check for it and handle correctly
1078        if ((sequenceInstance != null) &&
1079            (sequenceInstance.size() == expectedChildSequence.getChildren().size()))
1080        {
1081            sequenceInstance = null;
1082        }
1083       
1084        if (sequenceInstance == null) {
1085            //System.out.println("creating new sequence instance of selected child");
1086            sequenceInstance = taskFactory.createNewTaskInstance(expectedChildSequence);
1087               
1088            ITaskInstance replacementInstance = null;
1089           
1090            if (replacement instanceof IOptional) {
1091                replacementInstance = taskFactory.createNewTaskInstance((IOptional) replacement);
1092                taskBuilder.setChild((IOptionalInstance) replacementInstance, sequenceInstance);
1093            }
1094            else if (replacement instanceof ISelection) {
1095               
1096                /*System.out.println("replacement: ");
1097                new TaskTreeEncoder().encode(replacement, System.out);
1098               
1099                System.out.println("expectedChild: ");
1100                new TaskTreeEncoder().encode(expectedChildSequence, System.out);*/
1101               
1102                replacementInstance = taskFactory.createNewTaskInstance((ISelection) replacement);
1103                taskBuilder.setChild((ISelectionInstance) replacementInstance, sequenceInstance);
1104            }
1105           
1106            taskBuilder.addTaskInstance(session, replacementInstance);
1107        }
1108       
1109        return sequenceInstance;
1110    }
1111
1112    /**
1113     *
1114     */
1115    private void replaceTaskInstances(ITaskInstanceList                 taskInstanceList,
1116                                      Map<ITaskInstance, ITaskInstance> replacements,
1117                                      SimilarTasks                      similarTasks,
1118                                      IIteration                        harmonizedIteration,
1119                                      IOptional                         harmonizedOptional)
1120    {
1121        for (int i = 0; i < taskInstanceList.size(); i++) {
1122            ITaskInstance childInstance = taskInstanceList.get(i);
1123            ITaskInstance replacement = replacements.remove(childInstance);
1124           
1125            if (replacement != null) {
1126               
1127                // update the model for sequences (others are updated in the calling method)
1128                if (taskInstanceList instanceof ISequenceInstance) {
1129                    ISequence task = ((ISequenceInstance) taskInstanceList).getSequence();
1130                    taskBuilder.setChild(task, i, replacement.getTask());
1131                }
1132               
1133                // perform the actual replacement and throw away the instance
1134                taskBuilder.setTaskInstance(taskInstanceList, i, replacement);
1135                TaskPath path = new TaskPath();
1136                path.add(childInstance.getTask(), 0);
1137                discardTaskInstancesNotBelongingToTraversals(childInstance, path, similarTasks);
1138            }
1139            else {
1140                ITask modelUpdate = replaceTaskInstances(childInstance, replacements, similarTasks,
1141                                                         harmonizedIteration, harmonizedOptional);
1142               
1143                if (modelUpdate != null) {
1144                    if (taskInstanceList instanceof ISequenceInstance) {
1145                        taskBuilder.setChild
1146                            (((ISequenceInstance) taskInstanceList).getSequence(), i, modelUpdate);
1147                    }
1148                }
1149            }
1150        }
1151    }
1152
1153    /**
1154     *
1155     */
1156    private void replaceTaskInstances(IOptionalInstance                 optionalInstance,
1157                                      Map<ITaskInstance, ITaskInstance> replacements,
1158                                      SimilarTasks                      similarTasks,
1159                                      IIteration                        harmonizedIteration,
1160                                      IOptional                         harmonizedOptional)
1161    {
1162        ITaskInstance childInstance = optionalInstance.getChild();
1163       
1164        if (childInstance != null) {
1165            ITaskInstance replacement = replacements.remove(childInstance);
1166           
1167            if (replacement != null) {
1168                // do not update the model --> is updated in the calling method
1169               
1170                // perform the actual replacement and throw away the instance
1171                taskBuilder.setChild(optionalInstance, replacement);
1172                TaskPath path = new TaskPath();
1173                path.add(childInstance.getTask(), 0);
1174                discardTaskInstancesNotBelongingToTraversals(childInstance, path, similarTasks);
1175            }
1176            else {
1177                ITask modelUpdate = replaceTaskInstances(childInstance, replacements, similarTasks,
1178                                                         harmonizedIteration, harmonizedOptional);
1179               
1180                if (modelUpdate != null) {
1181                    taskBuilder.setMarkedTask(optionalInstance.getOptional(), modelUpdate);
1182                }
1183            }
1184        }
1185    }
1186
1187    /**
1188     *
1189     */
1190    private void replaceTaskInstances(ISelectionInstance                selectionInstance,
1191                                      Map<ITaskInstance, ITaskInstance> replacements,
1192                                      SimilarTasks                      similarTasks,
1193                                      IIteration                        harmonizedIteration,
1194                                      IOptional                         harmonizedOptional)
1195    {
1196        TaskComparator comparator = identTaskHandlStrat.getTaskComparator();
1197       
1198        ITaskInstance childInstance = selectionInstance.getChild();
1199       
1200        if (childInstance != null) {
1201            ITaskInstance replacement = replacements.remove(childInstance);
1202           
1203            if (replacement != null) {
1204               
1205                if (replacement instanceof ISelectionInstance) {
1206                    // if the replacement itself is a selection instance, we cannot add
1207                    // a selection instance as the child of the parent selection instance.
1208                    // therefore, we just use the child of the replacement as new child of the
1209                    // existing selection instance
1210                    taskBuilder.discardTaskInstance(replacement);
1211                    replacement = ((ISelectionInstance) replacement).getChild();
1212                }
1213               
1214                // update the model
1215                // we replace all instances of the merged tasks with instances of a new task.
1216                // hence, we also have to remove the task of the replaced children from the
1217                // available variants of the selection
1218                taskBuilder.removeChild(selectionInstance.getSelection(), childInstance.getTask());
1219
1220                boolean found = false;
1221                for (ITask child : selectionInstance.getSelection().getChildren()) {
1222                    if (comparator.equals(child, replacement.getTask())) {
1223                        found = true;
1224                        break;
1225                    }
1226                }
1227               
1228                if (!found) {
1229                    taskBuilder.addChild(selectionInstance.getSelection(), replacement.getTask());
1230                }
1231               
1232                // perform the actual replacement and throw away the instance
1233                taskBuilder.setChild(selectionInstance, replacement);
1234                TaskPath path = new TaskPath();
1235                path.add(childInstance.getTask(), 0);
1236                discardTaskInstancesNotBelongingToTraversals(childInstance, path, similarTasks);
1237            }
1238            else {
1239                ITask previousChildTask = childInstance.getTask();
1240                ITask modelUpdate = replaceTaskInstances(childInstance, replacements, similarTasks,
1241                                                         harmonizedIteration, harmonizedOptional);
1242               
1243                if (modelUpdate != null) {
1244                    taskBuilder.removeChild(selectionInstance.getSelection(), previousChildTask);
1245
1246                    boolean found = false;
1247                    for (ITask child : selectionInstance.getSelection().getChildren()) {
1248                        if (comparator.equals(child, modelUpdate)) {
1249                            found = true;
1250                            break;
1251                        }
1252                    }
1253                   
1254                    if (!found) {
1255                        taskBuilder.addChild(selectionInstance.getSelection(), modelUpdate);
1256                    }
1257                }
1258            }
1259        }
1260    }
1261
1262    /**
1263     *
1264     */
1265    private ITask replaceTaskInstances(ITaskInstance                     childInstance,
1266                                       Map<ITaskInstance, ITaskInstance> replacements,
1267                                       SimilarTasks                      similarTasks,
1268                                       IIteration                        harmonizedIteration,
1269                                       IOptional                         harmonizedOptional)
1270    {
1271        ITask modelUpdate = null;
1272       
1273        if (childInstance instanceof IIterationInstance) {
1274            ITask markedTask = ((IIterationInstance) childInstance).getIteration().getMarkedTask();
1275           
1276            if ((markedTask == similarTasks.getLeftHandSide()) ||
1277                (markedTask == similarTasks.getRightHandSide()))
1278            {
1279                taskBuilder.setTask(childInstance, harmonizedIteration);
1280                modelUpdate = harmonizedIteration;
1281            }
1282
1283            replaceTaskInstances((ITaskInstanceList) childInstance, replacements, similarTasks,
1284                                 harmonizedIteration, harmonizedOptional);
1285        }
1286        else if (childInstance instanceof IOptionalInstance) {
1287            ITask markedTask = ((IOptionalInstance) childInstance).getOptional().getMarkedTask();
1288           
1289            if ((markedTask == similarTasks.getLeftHandSide()) ||
1290                (markedTask == similarTasks.getRightHandSide()))
1291            {
1292                taskBuilder.setTask(childInstance, harmonizedOptional);
1293                modelUpdate = harmonizedOptional;
1294            }
1295           
1296            replaceTaskInstances((IOptionalInstance) childInstance, replacements, similarTasks,
1297                                 harmonizedIteration, harmonizedOptional);
1298        }
1299        else if (childInstance instanceof ISelectionInstance) {
1300            replaceTaskInstances((ISelectionInstance) childInstance, replacements, similarTasks,
1301                                 harmonizedIteration, harmonizedOptional);
1302        }
1303        else if (childInstance instanceof ISequenceInstance) {
1304            replaceTaskInstances((ITaskInstanceList) childInstance, replacements, similarTasks,
1305                                 harmonizedIteration, harmonizedOptional);
1306        }
1307       
1308        return modelUpdate;
1309    }
1310
1311    /**
1312     *
1313     */
1314    private void discardTaskInstancesNotBelongingToTraversals(ITaskInstance instance,
1315                                                              TaskPath      pathToInstance,
1316                                                              SimilarTasks  similarTasks)
1317    {
1318        boolean discarded = false;
1319        for (TaskPath candidate : similarTasks.getLeftHandSideTraversal().getTraversalPaths()) {
1320            if (candidate.size() > pathToInstance.size()) {
1321                TaskPath potentialEqualSubPath = candidate.subPath(0, pathToInstance.size());
1322                if (pathsMatch(potentialEqualSubPath, pathToInstance)) {
1323                    taskBuilder.discardTaskInstance(instance);
1324                    discarded = true;
1325                    break;
1326                }
1327            }
1328        }
1329       
1330        if (!discarded) {
1331            for (TaskPath candidate : similarTasks.getRightHandSideTraversal().getTraversalPaths()) {
1332                if (candidate.size() > pathToInstance.size()) {
1333                    TaskPath potentialEqualSubPath = candidate.subPath(0, pathToInstance.size());
1334                    if (pathsMatch(potentialEqualSubPath, pathToInstance)) {
1335                        taskBuilder.discardTaskInstance(instance);
1336                        discarded = true;
1337                        break;
1338                    }
1339                }
1340            }
1341        }
1342       
1343        if (discarded) {
1344            // now also discard the children
1345            if (instance instanceof ISequenceInstance) {
1346                int index = 0;
1347                for (ITaskInstance childInstance : (ITaskInstanceList) instance) {
1348                    pathToInstance.add(childInstance.getTask(), index++);
1349                    discardTaskInstancesNotBelongingToTraversals
1350                        (childInstance, pathToInstance, similarTasks);
1351                    pathToInstance.removeLast();
1352                }
1353            }
1354            else if (instance instanceof IIterationInstance) {
1355                for (ITaskInstance childInstance : (ITaskInstanceList) instance) {
1356                    pathToInstance.add(childInstance.getTask(), 0);
1357                    discardTaskInstancesNotBelongingToTraversals
1358                        (childInstance, pathToInstance, similarTasks);
1359                    pathToInstance.removeLast();
1360                }
1361            }
1362            else if (instance instanceof IOptionalInstance) {
1363                ITaskInstance childInstance = ((IOptionalInstance) instance).getChild();
1364                if (childInstance != null) {
1365                    pathToInstance.add(childInstance.getTask(), 0);
1366                    discardTaskInstancesNotBelongingToTraversals
1367                        (childInstance, pathToInstance, similarTasks);
1368                    pathToInstance.removeLast();
1369                }
1370            }
1371            else if (instance instanceof ISelectionInstance) {
1372                ITaskInstance childInstance = ((ISelectionInstance) instance).getChild();
1373                pathToInstance.add(childInstance.getTask(), 0);
1374                discardTaskInstancesNotBelongingToTraversals
1375                    (childInstance, pathToInstance, similarTasks);
1376                pathToInstance.removeLast();
1377            }
1378        }
1379    }
1380   
1381    /**
1382     *
1383     */
1384    private void mergeSubsequentIdenticalMarkingTemporalRelationships(ITaskInstanceList list) {
1385        int index = 0;
1386        TaskComparator comparator = identTaskHandlStrat.getTaskComparator();
1387       
1388        while (index < (list.size() - 1)) {
1389            ITaskInstance instance1 = list.get(index);
1390            ITaskInstance instance2 = list.get(index + 1);
1391           
1392            if (comparator.equals(instance1.getTask(), instance2.getTask())) {
1393                if (instance1 instanceof IIterationInstance) {
1394                    // add the children of the second to the first iteration instance and discard
1395                    // the second
1396                    for (ITaskInstance child : (IIterationInstance) instance2) {
1397                        taskBuilder.addChild((IIterationInstance) instance1, child);
1398                    }
1399                   
1400                    taskBuilder.removeTaskInstance(list, index + 1);
1401                    taskBuilder.discardTaskInstance(instance2);
1402                }
1403                else if (instance1 instanceof IOptionalInstance) {
1404                    ITaskInstance optionalChildInstance1 =
1405                        ((IOptionalInstance) instance1).getChild();
1406                    ITaskInstance optionalChildInstance2 =
1407                            ((IOptionalInstance) instance2).getChild();
1408                       
1409                    if (optionalChildInstance1 == null) {
1410                        // independent of the second, just remove the first. The second will be the
1411                        // unique representation
1412                        taskBuilder.removeTaskInstance(list, index);
1413                    }
1414                    else if (optionalChildInstance2 == null) {
1415                        // remove the second. The first will be the unique representation
1416                        taskBuilder.removeTaskInstance(list, index + 1);
1417                    }
1418                    else if (optionalChildInstance1 instanceof IIterationInstance) {
1419                        // add all children of the second optional iteration instance to the
1420                        // first and discard the second
1421                        for (ITaskInstance child : (IIterationInstance) optionalChildInstance2) {
1422                            taskBuilder.addChild
1423                                ((IIterationInstance) optionalChildInstance1, child);
1424                        }
1425                       
1426                        taskBuilder.removeTaskInstance(list, index + 1);
1427                        taskBuilder.discardTaskInstance(instance2);
1428                    }
1429                    else {
1430                        // both optional children are no iterations --> create an iteration
1431                        // for them and add them both as children.
1432                        throw new java.lang.UnsupportedOperationException("not implemented yet");
1433                    }
1434                }
1435                else {
1436                    index++;
1437                }
1438            }
1439            else {
1440                index++;
1441            }
1442        }
1443       
1444        for (ITaskInstance child : list) {
1445            if (child instanceof ITaskInstanceList) {
1446                mergeSubsequentIdenticalMarkingTemporalRelationships((ITaskInstanceList) child);
1447            }
1448        }
1449    }
1450   
1451    /**
1452     *
1453     */
1454    private static boolean pathsMatch(TaskPath path1, TaskPath path2) {
1455        if (path1 == null) {
1456            return path2 == null;
1457        }
1458        else if ((path2 != null) && (path1.size() == path2.size())) {
1459            for (int i = 0; i < path1.size(); i++) {
1460                if (!path1.get(i).equals(path2.get(i))) {
1461                    return false;
1462                }
1463            }
1464            return true;
1465        }
1466        else {
1467            return false;
1468        }
1469    }
1470
1471    /**
1472     *
1473     */
1474    /*private boolean containsNewTask(ITask task, RuleApplicationData appData) {
1475        if (appData.isSelfCreatedTask(task)) {
1476            return true;
1477        }
1478        else if (task instanceof IStructuringTemporalRelationship) {
1479            for (ITask child : ((IStructuringTemporalRelationship) task).getChildren()) {
1480                if (containsNewTask(child, appData)) {
1481                    return true;
1482                }
1483            }
1484        }
1485        else if (task instanceof IMarkingTemporalRelationship) {
1486            return containsNewTask(((IMarkingTemporalRelationship) task).getMarkedTask(), appData);
1487        }
1488       
1489        return false;
1490    }*/
1491
1492    /**
1493     *
1494     */
1495    private class RuleApplicationData {
1496
1497        /**
1498         *
1499         */
1500        private List<IUserSession> sessions;
1501       
1502        /**
1503         *
1504         */
1505        private ITaskModel taskModel;
1506
1507        /**
1508         *
1509         */
1510        private MostSimilarTaskDeterminer mostSimilarTasksDeterminer =
1511            new MostSimilarTaskDeterminer(identTaskHandlStrat.getTaskComparator());
1512       
1513        /**
1514         *
1515         */
1516        private List<SimilarTasks> mostSimilarTasks;
1517       
1518        /**
1519         *
1520         */
1521        private List<ITask> newTasks = new LinkedList<ITask>();
1522       
1523        /**
1524         *
1525         */
1526        private RuleApplicationResult ruleApplicationResult = new RuleApplicationResult();
1527       
1528        /**
1529         *
1530         */
1531        private RuleApplicationData(List<IUserSession> sessions) {
1532            this.sessions = sessions;
1533        }
1534
1535        /**
1536         *
1537         */
1538        boolean isSelfCreatedTask(ITask task) {
1539            for (ITask candidate : newTasks) {
1540                if (candidate == task) {
1541                    return true;
1542                }
1543            }
1544           
1545            return false;
1546        }
1547
1548        /**
1549         * @return the mostSimilarTasksDeterminer
1550         */
1551        private MostSimilarTaskDeterminer getMostSimilarTasksDeterminer() {
1552            return mostSimilarTasksDeterminer;
1553        }
1554
1555        /**
1556         *
1557         */
1558        private void markAsAlreadyCondensed(ITask task1, ITask task2) {
1559            mostSimilarTasksDeterminer.addComparisonToSkip(task1, task2);
1560        }
1561
1562        /**
1563         *
1564         */
1565        private RuleApplicationResult finalizeRuleApplicationResult() {
1566            for (ITask newTask : newTasks) {
1567                ruleApplicationResult.addNewlyCreatedTask(newTask);
1568               
1569                if (newTask instanceof IOptional) {
1570                    if (isSelfCreatedTask(((IOptional) newTask).getMarkedTask()) &&
1571                        (((IOptional) newTask).getMarkedTask() instanceof ISequence))
1572                    {
1573                        ruleApplicationResult.addNewlyCreatedTask
1574                            (((IOptional) newTask).getMarkedTask());
1575                    }
1576                }
1577                else if (newTask instanceof ISelection) {
1578                    for (ITask child : ((ISelection) newTask).getChildren()) {
1579                        if (isSelfCreatedTask(child) && (child instanceof ISequence)) {
1580                            ruleApplicationResult.addNewlyCreatedTask(child);
1581                        }
1582                    }
1583                }
1584            }
1585           
1586           
1587            return ruleApplicationResult;
1588        }
1589
1590        /**
1591         * @return the tree
1592         */
1593        private List<IUserSession> getSessions() {
1594            return sessions;
1595        }
1596
1597        /**
1598         * @return the taskModel
1599         */
1600        private ITaskModel getTaskModel() {
1601            return taskModel;
1602        }
1603
1604        /**
1605         * @param taskModel the taskModel to set
1606         */
1607        private void setTaskModel(ITaskModel taskModel) {
1608            this.taskModel = taskModel;
1609        }
1610
1611        /**
1612         * @return the orderedDiffLevels
1613         */
1614        private List<SimilarTasks> getMostSimilarTasks() {
1615            return mostSimilarTasks;
1616        }
1617
1618        /**
1619         * @param orderedDiffLevels the orderedDiffLevels to set
1620         */
1621        private void setMostSimilarTasks(List<SimilarTasks> mostSimilarTasks) {
1622            this.mostSimilarTasks = mostSimilarTasks;
1623        }
1624       
1625        /**
1626         *
1627         */
1628        private ITask ensureUnique(ITask task) {
1629            // replacements done in this rule are either optionals or selections. So focus on them
1630            if (task instanceof IOptional) {
1631                for (ITask newTask : newTasks) {
1632                    if (newTask instanceof IOptional) {
1633                        ITask child1 = ((IOptional) task).getMarkedTask();
1634                        ITask child2 = ((IOptional) newTask).getMarkedTask();
1635                        if (createdChildEquals(child1, child2)) {
1636                            // System.out.println("reusing optional " + newTask + " for " + task);
1637                            return newTask;
1638                        }
1639                    }
1640                }
1641            }
1642            else if (task instanceof ISelection) {
1643                for (ITask newTask : newTasks) {
1644                    if (newTask instanceof ISelection) {
1645                        List<ITask> children1 = ((ISelection) task).getChildren();
1646                        List<ITask> children2 = ((ISelection) newTask).getChildren();
1647                        if (createdSelectionChildrenEqual(children1, children2)) {
1648                            /*System.out.println("reusing selection " + newTask + " for " + task);
1649                           
1650                            System.out.println("---------------------------- existing task");
1651                            new TaskTreeEncoder().encode(newTask, System.out);
1652                           
1653                            System.out.println("---------------------------- completely new task");
1654                            new TaskTreeEncoder().encode(task, System.out);*/
1655                           
1656                            return newTask;
1657                        }
1658                    }
1659                }
1660            }
1661            else if (task instanceof ISequence) {
1662                List<ISequence> allRelevant = new ArrayList<>();
1663               
1664                for (ITask candidate : newTasks) {
1665                    if (candidate instanceof ISequence) {
1666                        allRelevant.add((ISequence) candidate);
1667                    }
1668                }
1669               
1670                for (ITask candidate : taskModel.getTasks()) {
1671                    if (candidate instanceof ISequence) {
1672                        allRelevant.add((ISequence) candidate);
1673                    }
1674                }
1675               
1676                for (ISequence candidate : allRelevant) {
1677                    List<ITask> children1 = ((ISequence) task).getChildren();
1678                    List<ITask> children2 = ((ISequence) candidate).getChildren();
1679
1680                    boolean equal = false;
1681                    if (children1.size() == children2.size()) {
1682                        equal = true;
1683                        for (int i = 0; i < children1.size(); i++) {
1684                            if (children1.get(i) != children2.get(i)) {
1685                                equal = false;
1686                                break;
1687                            }
1688                        }
1689                    }
1690
1691                    if (equal) {
1692                        // System.out.println("reusing sequence " + candidate + " for " + task);
1693                        return candidate;
1694                    }
1695                }
1696            }
1697           
1698            newTasks.add(task);
1699           
1700            return task;
1701        }
1702
1703        /**
1704         *
1705         */
1706        private boolean createdSelectionChildrenEqual(List<ITask> children1, List<ITask> children2) {
1707            if (children1.size() != children2.size()) {
1708                return false;
1709            }
1710           
1711            for (ITask child1 : children1) {
1712                boolean found = false;
1713               
1714                for (ITask child2 : children2) {
1715                    if (createdChildEquals(child1, child2)) {
1716                        found = true;
1717                        break;
1718                    }
1719                }
1720               
1721                if (!found) {
1722                    return false;
1723                }
1724            }
1725           
1726            return true;
1727        }
1728
1729        /**
1730         *
1731         */
1732        private boolean createdChildEquals(ITask child1, ITask child2) {
1733            if (child1 == child2) {
1734                return true;
1735            }
1736            else if (!isSelfCreatedTask(child1) && !isSelfCreatedTask(child2)) {
1737                // the simple comparison can not work if the tasks are not self created
1738                return false;
1739            }
1740            else if ((child1 instanceof ISequence) && (child2 instanceof ISequence)) {
1741                ISequence sequence1 = (ISequence) child1;
1742                ISequence sequence2 = (ISequence) child2;
1743               
1744                if (sequence1.getChildren().size() != sequence2.getChildren().size()) {
1745                    return false;
1746                }
1747               
1748                for (int i = 0; i < sequence1.getChildren().size(); i++) {
1749                    if (sequence1.getChildren().get(i) != sequence2.getChildren().get(i)) {
1750                        return false;
1751                    }
1752                }
1753               
1754                return true;
1755            }
1756            else {
1757                return false;
1758            }
1759        }
1760    }
1761   
1762    /**
1763     *
1764     */
1765    private static class FlattenInstruction {
1766       
1767        /**
1768         *
1769         */
1770        private enum Instruction { DONT_TRAVERSE, MAKE_OPTIONAL, MAKE_SELECTION, INTEGRATE_OPTIONAL };
1771
1772        /** */
1773        private TaskPath path;
1774
1775        /** */
1776        private TaskPath precedingPath;
1777
1778        /** */
1779        private Instruction instruction;
1780
1781        /** */
1782        private IOptional optional = null;
1783
1784        /** */
1785        private ISelection selection = null;
1786
1787        /** */
1788        private ITask selectedChild = null;
1789
1790        /**
1791         *
1792         */
1793        private FlattenInstruction(TaskPath path) {
1794            this.path = path;
1795            this.instruction = Instruction.DONT_TRAVERSE;
1796        }
1797
1798        /**
1799         *
1800         */
1801        private FlattenInstruction(TaskPath path, IOptional optional) {
1802            this.path = path;
1803            this.instruction = Instruction.MAKE_OPTIONAL;
1804            this.optional = optional;
1805        }
1806
1807        /**
1808         *
1809         */
1810        private FlattenInstruction(TaskPath path, ISelection selection, ITask selectedChild) {
1811            this.path = path;
1812            this.instruction = Instruction.MAKE_SELECTION;
1813            this.selection = selection;
1814            this.selectedChild = selectedChild;
1815        }
1816
1817        /**
1818         *
1819         */
1820        private FlattenInstruction(TaskPath  precedingPath,
1821                                   TaskPath  path,
1822                                   IOptional optional)
1823        {
1824            this.path = path;
1825            this.precedingPath = precedingPath;
1826            this.instruction = Instruction.INTEGRATE_OPTIONAL;
1827            this.optional = optional;
1828        }
1829       
1830        /**
1831         *
1832         */
1833        IOptional getOptional() {
1834            return optional;
1835        }
1836
1837
1838        /**
1839         * @return the selection
1840         */
1841        ISelection getSelection() {
1842            return selection;
1843        }
1844
1845        /**
1846         * @return the selectedChild
1847         */
1848        ITask getSelectedChild() {
1849            return selectedChild;
1850        }
1851
1852        /**
1853         * @return the instruction
1854         */
1855        Instruction getInstruction() {
1856            return instruction;
1857        }
1858
1859        /**
1860         * @return the precedingPath
1861         */
1862        TaskPath getPrecedingPath() {
1863            return precedingPath;
1864        }
1865
1866        /**
1867         *
1868         */
1869        boolean matches(TaskPath path) {
1870            return pathsMatch(this.path, path);
1871        }
1872       
1873
1874//        /**
1875//         *
1876//         */
1877//        void dump(PrintStream out) {
1878//            for (int i = 0; i < path.size(); i++) {
1879//                out.print("-->");
1880//                out.print(path.getTask(i).getId());
1881//                out.print('(');
1882//                out.print(path.get(i).getIndex());
1883//                out.print(')');
1884//            }
1885//           
1886//            out.print("  ");
1887//            out.print(instruction);
1888//           
1889//            out.println();
1890//        }
1891       
1892    }
1893
1894}
Note: See TracBrowser for help on using the repository browser.