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

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