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

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