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

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