source: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/utils/MostSimilarTaskDeterminer.java @ 1975

Last change on this file since 1975 was 1975, checked in by pharms, 9 years ago
  • prevented comparing parents with its children
File size: 32.7 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.utils;
16
17import java.util.HashMap;
18import java.util.HashSet;
19import java.util.Iterator;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.Map;
23import java.util.Set;
24import java.util.logging.Level;
25
26import de.ugoe.cs.autoquest.tasktrees.temporalrelation.TaskComparator;
27import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor;
28import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskTraversingVisitor;
29import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
30import de.ugoe.cs.autoquest.tasktrees.treeifc.IStructuringTemporalRelationship;
31import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
32import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInfo;
33import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
34import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstanceList;
35import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
36import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskMetric;
37import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskTreeUtils;
38import de.ugoe.cs.util.console.Console;
39
40/**
41 * <p>
42 * This class is a utility class for detecting similar tasks. It compares all tasks in a given
43 * list with each other and then chooses the pair with the highest level of similarity. For
44 * comparison, the tasks are traversed and only the traversals are compared with each other.
45 * </p>
46 * <p>
47 * Several task pairs may have the same similarity level. In this case, the class performs a
48 * filtering of the pairs to result in a merging order for the tasks that is always the same.
49 * </p>
50 * <p>
51 * If provided with many tasks, this class starts several threads to let the comparisons run in
52 * parallel.
53 * </p>
54 *
55 * @author Patrick Harms
56 */
57public class MostSimilarTaskDeterminer {
58
59    /**
60     * If this similarity level is exceeded, two tasks are not considered similar anymore. 33 means
61     * that at most 33% of the elements of both task traversals are not the same.
62     */
63    private static int MAX_DIFF_LEVEL = 33;
64   
65    /**
66     * task comparator used internally
67     */
68    private TaskComparator comparator;
69   
70    /**
71     * for performance reasons, some task comparisons can be excluded beforehand and the exclusions
72     * are stored in this list
73     */
74    private Set<Long> comparisonsToSkip = new HashSet<>();
75   
76    /** TODO comment */
77    private long comparisonCounter = 0;
78   
79    /** TODO comment */
80    private long effectiveComparisonCounter = 0;
81
82    /**
83     * Initialize instances of this class with the task comparator to be used
84     */
85    public MostSimilarTaskDeterminer(TaskComparator comparator) {
86        this.comparator = comparator;
87    }
88
89    /**
90     * add a pair of comparisons that is not done to increase performance
91     */
92    public void addComparisonToSkip(ITask task1, ITask task2) {
93        comparisonsToSkip.add(getMapId(task1, task2));
94    }
95
96    /**
97     * returns a list of most similar tasks. Independent of the order of the provided tasks, the
98     * returned list will always be the same for the same input elements.
99     */
100    public List<SimilarTasks> getMostSimilarTasks(List<ITask> tasks, ITaskModel taskModel) {
101        Console.println("comparing " + tasks.size() + " sequences with each other");
102
103        LinkedList<SimilarTasks> mostSimilarTasksList = performComparisons(taskModel, tasks);
104       
105        Console.println("initially found " + mostSimilarTasksList.size() + " similar sequences");
106       
107        applyFilterForSmallestDiffLevel(mostSimilarTasksList);
108       
109        Console.println("only " + mostSimilarTasksList.size() + " have the smallest diff level");
110       
111        applyFilterForParents(mostSimilarTasksList);
112       
113        Console.println(mostSimilarTasksList.size() + " remain after filtering for parents");
114       
115        applyFilterForMostCoveredEvents(mostSimilarTasksList, taskModel);
116       
117        Console.println("calculated " + mostSimilarTasksList.size() + " most similar sequences");
118
119        return mostSimilarTasksList;
120    }
121
122    /**
123     * filters the given list of similar tasks for those having the smallest diff level, which is
124     * in turn the highest similarity
125     */
126    private void applyFilterForSmallestDiffLevel(LinkedList<SimilarTasks> mostSimilarTasksList) {
127        // determine the smallest diff level
128        int smallestDiffLevel = Integer.MAX_VALUE;
129       
130        for (SimilarTasks candidate : mostSimilarTasksList) {
131            if (candidate.getDiffLevel() < smallestDiffLevel) {
132                smallestDiffLevel = candidate.getDiffLevel();
133            }
134        }
135       
136        if (smallestDiffLevel <= MAX_DIFF_LEVEL) {
137            // remove all entries with a higher diff level
138            Iterator<SimilarTasks> listIterator = mostSimilarTasksList.iterator();
139       
140            while (listIterator.hasNext()) {
141                if (listIterator.next().getDiffLevel() > smallestDiffLevel) {
142                    listIterator.remove();
143                }
144            }
145           
146            Console.println("smallest diff level is " + smallestDiffLevel);
147        }
148        else {
149            mostSimilarTasksList.clear();
150        }
151    }
152   
153    /**
154     * ensures that the given list of similar tasks does not contain two pairs, where one refers
155     * to a child task of another pair.
156     */
157    private void applyFilterForParents(LinkedList<SimilarTasks> mostSimilarTasksList) {
158       
159        // remove all entries being parents of another entry or where both tasks are
160        // generated through this rule
161        Iterator<SimilarTasks> listIterator = mostSimilarTasksList.iterator();
162        List<SimilarTasks> similarTasksToRemove = new LinkedList<SimilarTasks>();
163   
164        while (listIterator.hasNext()) {
165            SimilarTasks candidate = listIterator.next();
166           
167            ITask task1 = candidate.getLeftHandSide();
168            ITask task2 = candidate.getRightHandSide();
169            for (SimilarTasks potentialChild : mostSimilarTasksList) {
170                ITask task3 = potentialChild.getLeftHandSide();
171                ITask task4 = potentialChild.getRightHandSide();
172                if (TaskTreeUtils.isChild(task3, task1) ||
173                    TaskTreeUtils.isChild(task3, task2) ||
174                    TaskTreeUtils.isChild(task4, task1) ||
175                    TaskTreeUtils.isChild(task4, task2))
176                {
177                    similarTasksToRemove.add(candidate);
178                    break;
179                }
180            }
181        }
182       
183        listIterator = mostSimilarTasksList.iterator();
184       
185        while (listIterator.hasNext()) {
186            SimilarTasks candidate = listIterator.next();
187           
188            for (SimilarTasks toRemove : similarTasksToRemove) {
189                if (candidate == toRemove) {
190                    listIterator.remove();
191                }
192            }
193        }
194    }
195
196    /**
197     * performs a filtering of the detected similar tasks which ensures, that the list does not
198     * contain two pairs referring to the same task and that in such cases always the same pair
199     * will remain.
200     */
201    private void applyFilterForMostCoveredEvents(LinkedList<SimilarTasks> mostSimilarTasksList,
202                                                 ITaskModel               taskModel)
203    {
204        for (SimilarTasks similarTasks : mostSimilarTasksList) {
205            System.out.println(similarTasks.getLeftHandSide() + "  " + similarTasks.getRightHandSide());
206        }
207       
208        // check, if several remaining similar tasks refer to the same task
209        Map<ITask, LinkedList<SimilarTasks>> referredTasks =
210            new HashMap<ITask, LinkedList<SimilarTasks>>();
211       
212        for (SimilarTasks similarTasks : mostSimilarTasksList) {
213            ensureMapping(similarTasks.getLeftHandSide(), similarTasks, referredTasks);
214            ensureMapping(similarTasks.getRightHandSide(), similarTasks, referredTasks);
215        }
216       
217        // remove all entries of tasks occurring only once
218        List<ITask> tasksOccuringOnce = new LinkedList<ITask>();
219
220        for (Map.Entry<ITask, LinkedList<SimilarTasks>> entry : referredTasks.entrySet()) {
221            if (entry.getValue().size() <= 1) {
222                tasksOccuringOnce.add(entry.getKey());
223            }
224        }
225
226        for (ITask taskToRemove : tasksOccuringOnce) {
227            referredTasks.remove(taskToRemove);
228        }
229
230        // if there are remaining tasks occurring several times, try to extract one similar tasks
231        // object, that should be merged first
232
233        if (referredTasks.size() > 0) {
234            mostSimilarTasksList.clear();
235
236            Console.traceln(Level.FINEST, "several comparisons for the same task exist with " +
237                            "same diff level --> filtering for pair to be merged first");
238
239            SimilarTasks firstToMerge = null;
240
241            for (LinkedList<SimilarTasks> pairs : referredTasks.values()) {
242                for (SimilarTasks current : pairs) {
243                    if (firstToMerge == null) {
244                        firstToMerge = current;
245                    }
246                    else if (firstToMerge != current) {
247                        firstToMerge = getFirstToMerge(firstToMerge, current, taskModel);
248                    }
249                }
250            }
251           
252            if (firstToMerge != null) {
253                mostSimilarTasksList.add(firstToMerge);
254            }
255        }
256       
257    }
258
259    /**
260     * <p>
261     * compares two similar tasks and decides which of them is to be merged first.
262     * </p>
263     */
264    private SimilarTasks getFirstToMerge(SimilarTasks first,
265                                         SimilarTasks second,
266                                         ITaskModel   taskModel)
267    {
268       
269        int valFirst = getTaskMetric(first, TaskMetric.EVENT_COVERAGE, taskModel);
270        int valSecond = getTaskMetric(second, TaskMetric.EVENT_COVERAGE, taskModel);
271
272        if (valSecond > valFirst) {
273            return second;
274        }
275        else if (valSecond < valFirst) {
276            return first;
277        }
278
279        // no of covered events is equal, try to distinguish by count
280
281        valFirst = getTaskMetric(first, TaskMetric.COUNT, taskModel);
282        valSecond = getTaskMetric(second, TaskMetric.COUNT, taskModel);
283
284        if (valSecond > valFirst) {
285            return second;
286        }
287        else if (valSecond < valFirst) {
288            return first;
289        }
290
291        // count is equal, try to distinguish by depth
292
293        valFirst = getTaskMetric(first, TaskMetric.DEPTH, taskModel);
294        valSecond = getTaskMetric(second, TaskMetric.DEPTH, taskModel);
295
296        if (valSecond < valFirst) {
297            return second;
298        }
299        else if (valSecond > valFirst) {
300            return first;
301        }
302
303        // no of covered events is equal, try to distinguish by count
304
305        valFirst = cumulateTaskMetric(first, TaskMetric.COUNT, taskModel);
306        valSecond = cumulateTaskMetric(second, TaskMetric.COUNT, taskModel);
307
308        if (valSecond > valFirst) {
309            return second;
310        }
311        else if (valSecond < valFirst) {
312            return first;
313        }
314
315        // depth is equal. Calculate for both the similarity
316        // based on which the merging will take place
317        SimilarTasks tmp = SimilarTasks.getMergableLevelOfSimilarity(first, comparator);
318        valFirst = tmp != null ? tmp.getDiffLevel() : Integer.MAX_VALUE;
319           
320        tmp = SimilarTasks.getMergableLevelOfSimilarity(second, comparator);
321        valSecond = tmp != null ? tmp.getDiffLevel() : Integer.MAX_VALUE;
322
323        if (valSecond < valFirst) {
324            return second;
325        }
326        else if (valSecond > valFirst) {
327            return first;
328        }
329
330        first.dump(System.out);
331        second.dump(System.out);
332
333        throw new RuntimeException
334            ("several tasks are similar so that it is undecidable which to merge first");
335    }
336
337    /**
338     *
339     */
340    private void ensureMapping(ITask                                task,
341                               SimilarTasks                         similarTasks,
342                               Map<ITask, LinkedList<SimilarTasks>> map)
343    {
344        LinkedList<SimilarTasks> value = map.get(task);
345       
346        if (value == null) {
347            value = new LinkedList<SimilarTasks>();
348            map.put(task, value);
349        }
350       
351        value.add(similarTasks);
352    }
353
354
355    /**
356     * starts several threads performing the task comparisons
357     */
358    private LinkedList<SimilarTasks> performComparisons(ITaskModel taskModel, List<ITask> tasks) {
359        LinkedList<SimilarTasks> mostSimilarTasks = new LinkedList<SimilarTasks>();
360        List<Runnable> startedRunnables = new LinkedList<Runnable>();
361
362        comparisonCounter = 0;
363       
364        // groups size is minimal 100 or maximal 1000
365        int groupSize = Math.max
366            (100, Math.min(3000, 1 + (tasks.size() / Runtime.getRuntime().availableProcessors())));
367       
368        synchronized (startedRunnables) {
369            int start1 = 0;
370            int end1;
371            int start2;
372            int end2;
373           
374            do {
375                end1 = Math.min(start1 + groupSize, tasks.size());
376                TaskTraversal[] leftHandTraversals = new TaskTraversal[end1 - start1];
377               
378                Console.traceln(Level.FINE, "calculating left hand traversals from " + start1 +
379                                " to " + end1);
380               
381                int index = 0;
382                for (int j = start1; j < end1; j++) {
383                    leftHandTraversals[index++] =
384                        TaskTraversal.getTraversal(tasks.get(j), null);
385                }
386
387                start2 = 0;
388                do {
389                    end2 = Math.min(start2 + groupSize, end1 - 1);
390                    TaskTraversal[] rightHandTraversals = new TaskTraversal[end2 - start2];
391
392                    if (end2 <= start1) {
393                        Console.traceln(Level.FINE, "calculating right hand traversals from " +
394                                        start2 + " to " + end2);
395                       
396                        // traversals need to be created
397                        index = 0;
398                        for (int j = start2; j < end2; j++) {
399                            rightHandTraversals[index++] =
400                                TaskTraversal.getTraversal(tasks.get(j), null);
401                        }
402                       
403                    }
404                    else {
405                        // traversals can be reused
406                        Console.traceln(Level.FINE, "reusing traversals for right hand from " +
407                                        start2 + " to " + end2);
408                       
409                        rightHandTraversals = leftHandTraversals;
410                    }
411                   
412                    Runnable runnable = new CompareRunnable(taskModel, leftHandTraversals,
413                                                            rightHandTraversals,
414                                                            mostSimilarTasks, startedRunnables);
415                   
416                    while (startedRunnables.size() >=
417                               Math.max(1, Runtime.getRuntime().availableProcessors()))
418                    {
419                        try {
420                            Console.traceln(Level.FINER, "waiting for next thread to finish");
421                            startedRunnables.wait();
422                            Console.traceln(Level.FINER, "thread finished");
423                        }
424                        catch (InterruptedException e) {
425                            // should not happen
426                            Console.logException(e);
427                        }
428                    }
429               
430                    Console.traceln(Level.FINER, "starting next thread");
431                    startedRunnables.add(runnable);
432                    new Thread(runnable).start();
433                    Console.traceln(Level.FINER, "started next thread " + runnable);
434                   
435                    start2 = end2;
436                }
437                while (end2 < (end1 - 1));
438               
439                start1 = end1;
440            }
441            while (end1 < tasks.size());
442           
443           
444            while (startedRunnables.size() > 0) {
445                try {
446                    Console.traceln(Level.FINER, "waiting for next thread to finish");
447                    startedRunnables.wait();
448                    Console.traceln(Level.FINER, "thread finished");
449                }
450                catch (InterruptedException e) {
451                    // should not happen
452                    Console.logException(e);
453                }
454            }
455        }
456       
457        Console.traceln
458            (Level.FINER, "all threads finished, " + comparisonCounter + " comparisons done (" +
459             effectiveComparisonCounter + " effectively)");
460       
461        if (comparisonCounter != (((tasks.size() - 1) * tasks.size()) / 2)) {
462            throw new RuntimeException(comparisonCounter + "  " +
463                                       (((tasks.size() - 1) * tasks.size()) / 2));
464        }
465       
466        return mostSimilarTasks;
467    }
468
469    /**
470     * convenience method to get the value of a task metric
471     */
472    private int getTaskMetric(SimilarTasks similarTasks, TaskMetric metric, ITaskModel taskModel) {
473        if (similarTasks == null) {
474            return 0;
475        }
476       
477        ITaskInfo info1 = taskModel.getTaskInfo(similarTasks.getLeftHandSide());
478        ITaskInfo info2 = taskModel.getTaskInfo(similarTasks.getRightHandSide());
479       
480        return info1.getMeasureValue(metric) + info2.getMeasureValue(metric);
481    }
482   
483    /**
484     * convenience method to get the cumulative value of a task metric
485     */
486    private int cumulateTaskMetric(SimilarTasks     similarTasks,
487                                   final TaskMetric metric,
488                                   final ITaskModel taskModel)
489    {
490        final int[] value = new int[1];
491        value[0] = 0;
492       
493        DefaultTaskTraversingVisitor visitor = new DefaultTaskTraversingVisitor() {
494            @Override
495            public void visit(IStructuringTemporalRelationship relationship) {
496                value[0] += taskModel.getTaskInfo(relationship).getMeasureValue(metric);
497                super.visit(relationship);
498            }
499        };
500       
501        similarTasks.getLeftHandSide().accept(visitor);
502        similarTasks.getRightHandSide().accept(visitor);
503       
504        return value[0];
505    }
506
507    /**
508     * convenience method to check if a specific comparison shall be skipped
509     */
510    private boolean isComparisonToSkip(ITask task1, ITask task2) {
511        return comparisonsToSkip.contains(getMapId(task1, task2));
512    }
513
514    /**
515     * convenience method to get a unique id for representing a pair of two tasks
516     */
517    private long getMapId(ITask task1, ITask task2) {
518        if (task1.getId() < task2.getId()) {
519            return (((long) task1.getId()) << 32) + task2.getId();
520        }
521        else {
522            return (((long) task2.getId()) << 32) + task1.getId();
523        }
524    }
525
526    /**
527     * Runnable performing a subset of task comparisons in a dedicated thread
528     */
529    public class CompareRunnable implements Runnable {
530
531        /** */
532        private ITaskModel taskModel;
533       
534        /** */
535        private TaskTraversal[] leftHandTraversals;
536       
537        /** */
538        private TaskTraversal[] rightHandTraversals;
539       
540        /** */
541        private List<SimilarTasks> mostSimilarTasksList;
542
543        /** */
544        private List<Runnable> unfinishedRunnables;
545       
546        /**
547         *
548         */
549        public CompareRunnable(ITaskModel         taskModel,
550                               TaskTraversal[]    leftHandTraversals,
551                               TaskTraversal[]    rightHandTraversals,
552                               List<SimilarTasks> mostSimilarTasksList,
553                               List<Runnable>     unfinishedRunnables)
554        {
555            this.taskModel = taskModel;
556            this.leftHandTraversals = leftHandTraversals;
557            this.rightHandTraversals = rightHandTraversals;
558            this.mostSimilarTasksList = mostSimilarTasksList;
559            this.unfinishedRunnables = unfinishedRunnables;
560        }
561
562        /* (non-Javadoc)
563         * @see java.lang.Runnable#run()
564         */
565        @Override
566        public void run() {
567            SimilarTasks mostSimilarTasks = SimilarTasks.UNEQUAL_TASKS;
568            int mostSimilarDiffLevel = MAX_DIFF_LEVEL;
569            List<SimilarTasks> allMostSimilarTasks = new LinkedList<SimilarTasks>();
570            int counter = 0;
571            int effectiveComparisons = 0;
572           
573            LEFT_HAND_TRAVERSAL:
574            for (int i = 0; i < leftHandTraversals.length; i++) {
575               
576                ITask leftHandTask = leftHandTraversals[i].getTask();
577               
578                RIGHT_HAND_TRAVERSAL:
579                for (int j = 0; j < rightHandTraversals.length; j++) {
580
581                    ITask rightHandTask = rightHandTraversals[j].getTask();
582                   
583                    try {
584                        if (leftHandTask == rightHandTask) {
585                            continue LEFT_HAND_TRAVERSAL;
586                        }
587
588                        counter++;
589
590                        if (isComparisonToSkip(leftHandTask, rightHandTask)) {
591                            continue RIGHT_HAND_TRAVERSAL;
592                        }
593                       
594                        if (!isBasicallySimilar(leftHandTraversals[i], rightHandTraversals[j])) {
595                            continue RIGHT_HAND_TRAVERSAL;
596                        }
597                       
598                        effectiveComparisons++;
599
600                        SimilarTasks similarTasks1 = SimilarTasks.compareTraversals
601                            (leftHandTraversals[i], rightHandTraversals[j], comparator);
602
603                        SimilarTasks similarTasks2 = SimilarTasks.compareTraversals
604                            (rightHandTraversals[j], leftHandTraversals[i], comparator);
605
606                        if ((similarTasks1.getDiffLevel() <= mostSimilarDiffLevel) ||
607                            (similarTasks2.getDiffLevel() <= mostSimilarDiffLevel))
608                        {
609                            SimilarTasks similarTasks =
610                                getSimilarTasksToPrefer(similarTasks1, similarTasks2);
611
612                            if (similarTasks.isInBetweenDifference() ||
613                                (similarTasks.getPatch().getDeltas().size() == 0))
614                            {
615                                if (similarTasks.getDiffLevel() < mostSimilarDiffLevel) {
616                                    mostSimilarTasks = similarTasks;
617                                    mostSimilarDiffLevel = mostSimilarTasks.getDiffLevel();
618                                    allMostSimilarTasks.clear();
619                                    allMostSimilarTasks.add(similarTasks);
620                                }
621                                else if (similarTasks.getDiffLevel() == mostSimilarDiffLevel) {
622                                    allMostSimilarTasks.add(similarTasks);
623                                }
624                            }
625                        }
626                    }
627                    catch (Exception e) {
628                        e.printStackTrace();
629
630                        SimilarTasks similarTasks1 = SimilarTasks.compareTraversals
631                            (leftHandTraversals[i], rightHandTraversals[j], comparator);
632
633                        SimilarTasks similarTasks2 = SimilarTasks.compareTraversals
634                            (rightHandTraversals[j], leftHandTraversals[i], comparator);
635
636                        similarTasks1.dump(System.err);
637
638                        similarTasks2.dump(System.err);
639                    }
640                }
641            }
642           
643            synchronized (unfinishedRunnables) {
644                mostSimilarTasksList.addAll(allMostSimilarTasks);
645                comparisonCounter += counter;
646                effectiveComparisonCounter += effectiveComparisons;
647               
648                for (int i = 0; i < unfinishedRunnables.size(); i++) {
649                    if (unfinishedRunnables.get(i) == this) {
650                        unfinishedRunnables.remove(i);
651                        unfinishedRunnables.notify();
652                    }
653                }
654            }
655        }
656
657        /**
658         *
659         */
660        private boolean isBasicallySimilar(TaskTraversal traversal1, TaskTraversal traversal2) {
661            int length1 = traversal1.size();
662            int length2 = traversal2.size();
663            int maxLength = Math.max(length1, length2);
664            int lengthDiff = 100 * Math.abs(length1 - length2) / maxLength;
665           
666            if (lengthDiff > MAX_DIFF_LEVEL) {
667                return false;
668            }
669            else {
670                ITask firstTask1 = traversal1.get(0);
671                ITask firstTask2 = traversal2.get(0);
672               
673                if (!comparator.equals(firstTask1, firstTask2)) {
674                    return false;
675                }
676               
677                ITask lastTask1 = traversal1.get(traversal1.size() - 1);
678                ITask lastTask2 = traversal2.get(traversal2.size() - 1);
679               
680                if (!comparator.equals(lastTask1, lastTask2)) {
681                    return false;
682                }
683               
684                if (TaskTreeUtils.isChild(traversal1.getTask(), traversal2.getTask()) ||
685                    TaskTreeUtils.isChild(traversal2.getTask(), traversal1.getTask()))
686                {
687                    return false;
688                }
689               
690                return true;
691            }
692        }
693
694        /**
695         *
696         */
697        private SimilarTasks getSimilarTasksToPrefer(SimilarTasks similarTasks1,
698                                                     SimilarTasks similarTasks2)
699        {
700            if (similarTasks2.getDiffLevel() > similarTasks1.getDiffLevel()) {
701                return similarTasks2;
702            }
703            else if (similarTasks1.getDiffLevel() > similarTasks2.getDiffLevel()) {
704                return similarTasks1;
705            }
706            else if (similarTasks1.getDiffLevel() == similarTasks2.getDiffLevel()) {
707                ITask first = similarTasks1.getLeftHandSide();
708                ITask second = similarTasks2.getLeftHandSide();
709               
710                long valFirst = getTaskMetric(first, TaskMetric.EVENT_COVERAGE);
711                long valSecond = getTaskMetric(second, TaskMetric.EVENT_COVERAGE);
712
713                if (valSecond > valFirst) {
714                    return similarTasks2;
715                }
716                else if (valSecond < valFirst) {
717                    return similarTasks1;
718                }
719
720                // no of covered events is equal, try to distinguish by count
721
722                valFirst = getTaskMetric(first, TaskMetric.COUNT);
723                valSecond = getTaskMetric(second, TaskMetric.COUNT);
724
725                if (valSecond > valFirst) {
726                    return similarTasks2;
727                }
728                else if (valSecond < valFirst) {
729                    return similarTasks1;
730                }
731
732                // count is equal, try to distinguish by depth
733
734                valFirst = getTaskMetric(first, TaskMetric.DEPTH);
735                valSecond = getTaskMetric(second, TaskMetric.DEPTH);
736
737                if (valSecond < valFirst) {
738                    return similarTasks2;
739                }
740                else if (valSecond > valFirst) {
741                    return similarTasks1;
742                }
743
744                // no of covered events is equal, try to distinguish by count
745
746                valFirst = cumulateTaskMetric(first, TaskMetric.COUNT);
747                valSecond = cumulateTaskMetric(second, TaskMetric.COUNT);
748
749                if (valSecond > valFirst) {
750                    return similarTasks2;
751                }
752                else if (valSecond < valFirst) {
753                    return similarTasks1;
754                }
755
756                // depth is equal. Calculate for both the similarity
757                // based on which the merging will take place
758                SimilarTasks tmp =
759                    SimilarTasks.getMergableLevelOfSimilarity(similarTasks1, comparator);
760               
761                valFirst = tmp != null ? tmp.getDiffLevel() : Integer.MAX_VALUE;
762               
763                tmp = SimilarTasks.getMergableLevelOfSimilarity(similarTasks2, comparator);
764                valSecond = tmp != null ? tmp.getDiffLevel() : Integer.MAX_VALUE;
765
766                if (valSecond < valFirst) {
767                    return similarTasks2;
768                }
769                else if (valSecond > valFirst) {
770                    return similarTasks1;
771                }
772
773                valFirst = getFirstTimestamp(first);
774                valSecond = getFirstTimestamp(second);
775               
776                if (valSecond > valFirst) {
777                    return similarTasks1;
778                }
779                else if (valSecond < valFirst) {
780                    return similarTasks2;
781                }
782
783                similarTasks1.dump(System.out);
784                similarTasks2.dump(System.out);
785
786                throw new RuntimeException
787                    ("several tasks are similar so that it is undecidable which to merge first");
788            }
789           
790            return null;
791        }
792
793        /**
794         *
795         */
796        private int getTaskMetric(ITask task, TaskMetric metric) {
797            ITaskInfo info = taskModel.getTaskInfo(task);
798            return info.getMeasureValue(metric);
799        }
800       
801        /**
802         * convenience method to get the cumulative value of a task metric
803         */
804        private int cumulateTaskMetric(ITask            task,
805                                       final TaskMetric metric)
806        {
807            final int[] value = new int[1];
808            value[0] = 0;
809           
810            DefaultTaskTraversingVisitor visitor = new DefaultTaskTraversingVisitor() {
811                @Override
812                public void visit(IStructuringTemporalRelationship relationship) {
813                    value[0] += taskModel.getTaskInfo(relationship).getMeasureValue(metric);
814                    super.visit(relationship);
815                }
816            };
817           
818            task.accept(visitor);
819           
820            return value[0];
821        }
822
823        /**
824         *
825         */
826        private long getFirstTimestamp(ITask task) {
827            long timestamp = Long.MAX_VALUE;
828           
829            final List<IEventTaskInstance> eventTaskInstances = new LinkedList<>();
830            for (ITaskInstance instance : task.getInstances()) {
831                eventTaskInstances.clear();
832               
833                instance.accept(new DefaultTaskInstanceTraversingVisitor() {
834
835                    @Override
836                    public void visit(IEventTaskInstance eventTaskInstance) {
837                        eventTaskInstances.add(eventTaskInstance);
838                    }
839
840                    @Override
841                    public void visit(ITaskInstanceList taskInstanceList) {
842                        for (ITaskInstance child : taskInstanceList) {
843                            if (eventTaskInstances.size() > 0) {
844                                break;
845                            }
846                           
847                            child.accept(this);
848                        }
849                    }
850                   
851                });
852               
853                if (eventTaskInstances.size() > 0) {
854                    long newTimestamp =
855                        ((IEventTaskInstance) eventTaskInstances.get(0)).getEvent().getTimestamp();
856                   
857                    if (timestamp > newTimestamp) {
858                        timestamp = newTimestamp;
859                    }
860                }
861            }
862           
863            return timestamp;
864        }
865       
866        /* (non-Javadoc)
867         * @see java.lang.Object#toString()
868         */
869        @Override
870        public String toString() {
871            return "CompareThread(" + leftHandTraversals.length + ", " +
872                rightHandTraversals.length + ")";
873        }
874
875    }
876}
Note: See TracBrowser for help on using the repository browser.