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

Last change on this file since 1890 was 1890, checked in by pharms, 9 years ago
  • extended task similarity check to handle identical tasks also correctly
File size: 43.4 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.io.PrintStream;
18import java.util.HashSet;
19import java.util.LinkedList;
20import java.util.List;
21import java.util.Set;
22
23import de.ugoe.cs.autoquest.eventcore.gui.Scroll;
24import de.ugoe.cs.autoquest.tasktrees.temporalrelation.TaskComparator;
25import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskTraversingVisitor;
26import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
27import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
28import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
29import de.ugoe.cs.autoquest.tasktrees.treeifc.IMarkingTemporalRelationship;
30import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptional;
31import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
32import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
33import de.ugoe.cs.autoquest.tasktrees.treeifc.IStructuringTemporalRelationship;
34import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
35import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
36import de.ugoe.cs.autoquest.tasktrees.treeifc.ITemporalRelationship;
37import de.ugoe.cs.autoquest.tasktrees.treeifc.TaskPath;
38import difflib.Chunk;
39import difflib.Delta;
40import difflib.Patch;
41
42/**
43 * <p>
44 * This class represents a pair of tasks and their corresponding similarity level, respectively,
45 * their diff level. In addition, it contains the task traversal, i.e., the flattened variants
46 * of the compared tasks. It provides static methods to compare two tasks and get an object of this
47 * class as result. Furthermore, it allows to adapt the contained traversals so that unmergable
48 * subtasks are not traversed.
49 * </p>
50 *
51 * @author Patrick Harms
52 */
53public class SimilarTasks {
54   
55    /**
56     * default indicator for unequal tasks
57     */
58    public static final SimilarTasks UNEQUAL_TASKS = new SimilarTasks(null, null, null);
59   
60    /**
61     * result of the string like comparison of both task traversals
62     */
63    private Patch patch;
64   
65    /**
66     * the diff level resulting from the comparison
67     */
68    private int diffLevel;
69   
70    /**
71     * the first compared task
72     */
73    private ITask leftHandSide;
74   
75    /**
76     * the second compare task
77     */
78    private ITask rightHandSide;
79   
80    /**
81     * the traversal of the first task
82     */
83    private TaskTraversal leftHandSideTraversal;
84   
85    /**
86     * the traversal of the second task
87     */
88    private TaskTraversal rightHandSideTraversal;
89   
90    /**
91     * initialized a pair of tasks and calculates its diff level
92     */
93    public SimilarTasks(Patch         patch,
94                        TaskTraversal traversal1,
95                        TaskTraversal traversal2)
96    {
97        if (traversal1 != null) {
98            this.leftHandSide = traversal1.getTask();
99        }
100       
101        this.leftHandSideTraversal = traversal1;
102       
103        if (traversal2 != null) {
104            this.rightHandSide = traversal2.getTask();
105        }
106       
107        this.rightHandSideTraversal = traversal2;
108       
109        if (patch != null) {
110            this.patch = patch;
111            this.diffLevel = getDiffLevel(patch, traversal1, traversal2);
112        }
113        else {
114            this.diffLevel = 100;
115        }
116    }
117
118    /**
119     * static convenience method to compare two tasks
120     */
121    public static SimilarTasks compareTasks(ITask          task1,
122                                            ITask          task2,
123                                            TaskComparator comparator)
124    {
125        return compareTasks(task1, task2, comparator, null);
126    }
127
128    /**
129     * static convenience method to compare two tasks but ignoring a given set of subtasks when
130     * creating the traversals.
131     */
132    public static SimilarTasks compareTasks(ITask          task1,
133                                            ITask          task2,
134                                            TaskComparator comparator,
135                                            List<TaskPath> pathsNotToTraverse)
136    {
137        TaskTraversal traversal1 = TaskTraversal.getTraversal(task1, pathsNotToTraverse);
138        TaskTraversal traversal2 = TaskTraversal.getTraversal(task2, pathsNotToTraverse);
139
140        if ((traversal1.size() <= 0) || (traversal2.size() <= 0)) {
141            return null;
142        }
143       
144        return compareTraversals(traversal1, traversal2, comparator);
145    }
146
147    /**
148     * <p>
149     * TODO: comment
150     * </p>
151     *
152     * @param traversal1
153     * @param traversal2
154     * @param comparator
155     * @return
156     */
157    public static SimilarTasks compareTraversals(TaskTraversal  traversal1,
158                                                 TaskTraversal  traversal2,
159                                                 TaskComparator comparator)
160    {
161        Patch patch = new TaskTraversalMyersDiff(comparator).getDiff(traversal1, traversal2);
162
163        SimilarTasks result = new SimilarTasks(patch, traversal1, traversal2);
164
165        return result;
166    }
167
168
169    /**
170     * This method checks for mergability of the provided similar tasks and if this is not given,
171     * it reduces the traversals by ignoring interleaving iterations and other situations until
172     * the remaining traversals can be the basis of a merge.
173     */
174    public static SimilarTasks getMergableLevelOfSimilarity(SimilarTasks   source,
175                                                            TaskComparator comparator)
176    {
177        SimilarTasks similarTasks = source;
178       
179        List<TaskPath> pathsToIgnore = similarTasks.getPathsToIgnore();
180       
181        int prevPathsToIgnoreCount = 0;
182       
183        do {
184            prevPathsToIgnoreCount = pathsToIgnore.size();
185           
186            similarTasks = compareTasks
187                (similarTasks.getLeftHandSide(), similarTasks.getRightHandSide(),
188                 comparator, pathsToIgnore);
189
190            // similarTasks.dump(System.out);
191           
192            List<TaskPath> furtherPathsToIgnore = similarTasks.getPathsToIgnore();
193           
194            // System.out.println("further paths to ignore:");
195            // for (TaskPath path : furtherPathsToIgnore) {
196            //     System.out.println("  " + path);
197            // }
198           
199            for (TaskPath pathToAdd : furtherPathsToIgnore) {
200                boolean found = false;
201                for (TaskPath pathToIgnore : pathsToIgnore) {
202                    if (pathToAdd.equals(pathToIgnore)) {
203                        found = true;
204                        break;
205                    }
206                }
207               
208                if (!found) {
209                    pathsToIgnore.add(pathToAdd);
210                }
211            }
212        }
213        while (pathsToIgnore.size() > prevPathsToIgnoreCount);
214       
215        if (similarTasks.isStillWellAligned()) {
216            return similarTasks;
217        }
218        else {
219            // this may happen, if due to interleaving iterations the similarities of the task
220            // move and the diff algorithm does not align them anymore as optimal as possible
221            // because some similarities were lost due to not comparing all paths anymore.
222            return null;
223        }
224    }
225
226    /**
227     * checks if the delta between the two traversals is at their border or not
228     */
229    public boolean isInBetweenDifference() {
230        List<Delta> deltas = patch.getDeltas();
231       
232        for (Delta delta : deltas) {
233            int origPos = delta.getOriginal().getPosition();
234            int origSize = delta.getOriginal().size();
235            int revPos = delta.getRevised().getPosition();
236            int revSize = delta.getRevised().size();
237           
238            if ((origPos <= 0) || (revPos <= 0)) {
239                // optional must not be the beginning of the task
240                return false;
241            }
242            else if ((delta.getType() == Delta.TYPE.INSERT) &&
243                     ((revPos + revSize) >= rightHandSideTraversal.size()))
244            {
245                // optional must not be the end of the right hand side task
246                return false;
247            }
248            else if ((delta.getType() == Delta.TYPE.DELETE) &&
249                     ((origPos + origSize) >= leftHandSideTraversal.size()))
250            {
251                // optional must not be the end of the left hand side task
252                return false;
253            }
254            else if ((delta.getType() == Delta.TYPE.CHANGE) &&
255                     (((revPos + revSize) >= rightHandSideTraversal.size()) ||
256                      ((origPos + origSize) >= leftHandSideTraversal.size())))
257           {
258               // selection must not be the end of the left hand or right hand side task
259               return false;
260           }
261        }
262       
263        return deltas.size() > 0;
264    }
265
266    /**
267     * <p>
268     * TODO: comment
269     * </p>
270     *
271     * @return
272     */
273    public boolean isStillWellAligned() {
274        if (isInBetweenDifference()) {
275            return true;
276        }
277        else {
278            for (Delta delta : patch.getDeltas()) {
279                if (delta.getType() == Delta.TYPE.INSERT) {
280                    if ((delta.getOriginal().getPosition() == 0) ||
281                        (delta.getOriginal().getPosition() == leftHandSideTraversal.size()))
282                    {
283                        return false;
284                    }
285                }
286                else if (delta.getType() == Delta.TYPE.DELETE) {
287                    if ((delta.getRevised().getPosition() == 0) ||
288                        (delta.getRevised().getPosition() == rightHandSideTraversal.size()))
289                    {
290                        return false;
291                    }
292                }
293            }
294        }
295       
296        return true;
297    }
298   
299    /**
300     * @return the patch
301     */
302    public Patch getPatch() {
303        return patch;
304    }
305
306    /**
307     * @return the diff level
308     */
309    public int getDiffLevel() {
310        return diffLevel;
311    }
312
313    /**
314     * @return the first task of the comparison
315     */
316    public ITask getLeftHandSide() {
317        return leftHandSide;
318    }
319
320    /**
321     * @return the second task of the comparison
322     */
323    public ITask getRightHandSide() {
324        return rightHandSide;
325    }
326
327    /**
328     * @return the traversal of the first task of the comparison
329     */
330    public TaskTraversal getLeftHandSideTraversal() {
331        return leftHandSideTraversal;
332    }
333
334    /**
335     * @return the traversal of the second task of the comparison
336     */
337    public TaskTraversal getRightHandSideTraversal() {
338        return rightHandSideTraversal;
339    }
340
341    /**
342     * returns paths to subtasks, that can not be merged.
343     */
344    public List<TaskPath> getPathsToIgnore() {
345        List<TaskPath> result = new LinkedList<TaskPath>();
346        Delta singleChangeDelta = null;
347       
348        if ((patch.getDeltas().size() == 1) &&
349            (patch.getDeltas().get(0).getType() == Delta.TYPE.CHANGE))
350        {
351            singleChangeDelta = patch.getDeltas().get(0);
352        }
353       
354        // if it is a full change, then the parent of either side must be fully ignored
355        if (singleChangeDelta != null) {
356            if ((singleChangeDelta.getOriginal().getPosition() == 0) &&
357                (singleChangeDelta.getOriginal().size() == leftHandSideTraversal.size()))
358            {
359                TaskPath path = new TaskPath();
360                path.add(leftHandSide, 0);
361                result.add(path);
362            }
363       
364            if ((singleChangeDelta.getRevised().getPosition() == 0) &&
365                (singleChangeDelta.getRevised().size() == rightHandSideTraversal.size()))
366            {
367                TaskPath path = new TaskPath();
368                path.add(rightHandSide, 0);
369                result.add(path);
370            }
371        }
372       
373        if (result.size() < 2) {
374            // if we do not already ignore both root tasks, check for selections and interleaving
375            // iterations that have to be ignored if they belong to a delta
376            for (Delta delta : patch.getDeltas()) {
377                //getSelections(delta, result);
378                getPathsToRealIntersectingIterations(delta, result);
379                getNotFullyInterleavingIteration(delta, result);
380                getPathsToParentTasksFullyInsideDelta(delta, result);
381            }
382           
383            // check for any parent optional that may be empty and can hence not be flattened
384            TaskPath tmppath = new TaskPath();
385            tmppath.add(leftHandSide, 0);
386            getParentOptionals(tmppath, result);
387            tmppath = new TaskPath();
388            tmppath.add(rightHandSide, 0);
389            getParentOptionals(tmppath, result);
390           
391            // check if there are common subtasks on both sides that lie outside any delta and
392            // that hence should be ignored (especially when performing flattening of task
393            // instances later with the goal to preserve as many subtasks and respective instances)
394            int deltaIndex = 0;
395            int leftTraversalIndex = 0;
396            int rightTraversalIndex = 0;
397            Set<ITask> commonInvolvedTasks = getCommonInvolvedTasks(leftHandSide, rightHandSide);
398           
399            do {
400                Delta delta = deltaIndex < patch.getDeltas().size()
401                    ? patch.getDeltas().get(deltaIndex++) : null;
402               
403                int nextLeftTraversalIndex = leftHandSideTraversal.size();
404                int nextRightTraversalIndex = rightHandSideTraversal.size();
405               
406                if (delta != null) {
407                    nextLeftTraversalIndex = delta.getOriginal().getPosition() + 1;
408                    nextRightTraversalIndex = delta.getRevised().getPosition() + 1;
409                   
410                    if (delta.getType() == Delta.TYPE.INSERT) {
411                        nextLeftTraversalIndex--;
412                    }
413                    else if (delta.getType() == Delta.TYPE.DELETE) {
414                        nextRightTraversalIndex--;
415                    }
416                }
417               
418                TaskPath[] leftSubTraversal =
419                    leftHandSideTraversal.subList(leftTraversalIndex, nextLeftTraversalIndex);
420               
421                for (TaskPath path : leftSubTraversal) {
422                    for (int i = 0; i < path.size(); i++) {
423                        if (commonInvolvedTasks.contains(path.getTask(i))) {
424                            // found a candidate. Check if the candidate does not span more than
425                            // the sub traversal
426                            TaskPath parentPath = path.subPath(0, i + 1);
427                            int[] indexes = leftHandSideTraversal.getIndexesRootedBy(parentPath);
428                           
429                            if ((indexes[0] >= leftTraversalIndex) &&
430                                (indexes[1] < nextLeftTraversalIndex))
431                            {
432                                result.add(parentPath);
433                                break; // breaks only the path but the rest of the traversal is
434                                       // checked too
435                            }
436                        }
437                    }
438                }
439               
440                TaskPath[] rightSubTraversal =
441                    rightHandSideTraversal.subList(rightTraversalIndex, nextRightTraversalIndex);
442               
443                for (TaskPath path : rightSubTraversal) {
444                    for (int i = 0; i < path.size(); i++) {
445                        if (commonInvolvedTasks.contains(path.getTask(i))) {
446                            // found a candidate. Check if the candidate does not span more than
447                            // the sub traversal
448                            TaskPath parentPath = path.subPath(0, i + 1);
449                            int[] indexes = rightHandSideTraversal.getIndexesRootedBy(parentPath);
450                           
451                            if ((indexes[0] >= rightTraversalIndex) &&
452                                (indexes[1] < nextRightTraversalIndex))
453                            {
454                                result.add(parentPath);
455                                break; // breaks only the path but the rest of the traversal is
456                                       // checked too
457                            }
458                        }
459                    }
460                }
461               
462                leftTraversalIndex = nextLeftTraversalIndex;
463                rightTraversalIndex = nextRightTraversalIndex;
464            }
465            while (leftTraversalIndex < leftHandSideTraversal.size());
466           
467         }
468       
469        return result;
470    }
471
472    /**
473     *
474     */
475    public void dump(PrintStream out) {
476        out.println();
477        out.print("similar tasks: left ");
478        out.print(leftHandSide);
479        out.print(" (");
480        out.print(leftHandSide.getInstances().size());
481        out.print(" instances), right ");
482        out.print(rightHandSide);
483        out.print(" (");
484        out.print(rightHandSide.getInstances().size());
485        out.print(" instances), diff level ");
486        out.println(diffLevel);
487       
488        out.println("left traversal");
489        for (TaskPath path : leftHandSideTraversal.getTraversalPaths()) {
490            out.println(path);
491        }
492       
493        out.println("right traversal");
494        for (TaskPath path : rightHandSideTraversal.getTraversalPaths()) {
495            out.println(path);
496        }
497       
498        if (patch != null) {
499            out.println("deltas:");
500            for (Delta delta : patch.getDeltas()) {
501                out.println(delta);
502            }
503        }
504       
505        List<String> linesLeft = new LinkedList<String>();
506        TaskPath path = new TaskPath();
507        path.add(leftHandSide, 0);
508        dumpToLineList(path, leftHandSideTraversal, linesLeft);
509        path.removeLast();
510       
511        List<String> linesRight = new LinkedList<String>();
512        path.add(rightHandSide, 0);
513        dumpToLineList(path, rightHandSideTraversal, linesRight);
514
515       
516        /*System.out.println("\n#################################################");
517        for (int j = 0; ((j < linesLeft.size()) || (j < linesRight.size())); j++) {
518            String left = j < linesLeft.size() ? linesLeft.get(j) : "";
519            String right = j < linesRight.size() ? linesRight.get(j) : "";
520           
521            StringBuffer line = new StringBuffer();
522            line.append(j);
523            line.append(":\t");
524            line.append(left);
525           
526            for (int k = line.length(); k < 60; k++) {
527                line.append(' ');
528            }
529           
530            line.append(right);
531            System.out.println(line);
532        }*/
533
534        // change lists to have the same length depending on the changes
535        int lineIndexLeft = 0;
536        int lineIndexRight = 0;
537        int leftHandTraversalIndex = 0;
538        while (leftHandTraversalIndex < leftHandSideTraversal.size()) {
539           
540            //System.out.println("\n" + lineIndexLeft + "   " + lineIndexRight);
541           
542            Delta delta = null;
543           
544            for (Delta candidate : patch.getDeltas()) {
545                if (candidate.getOriginal().getPosition() == leftHandTraversalIndex) {
546                    delta = candidate;
547                    break;
548                }
549            }
550           
551            if ((delta != null) && (delta.getType() == Delta.TYPE.DELETE)) {
552                for (int j = 0; j < delta.getOriginal().size(); j++) {
553                    while (linesLeft.get(lineIndexLeft) != null) {
554                        lineIndexLeft++;
555                    }
556                   
557                    linesLeft.remove(lineIndexLeft);
558                   
559                    while (lineIndexRight < lineIndexLeft) {
560                        linesRight.add(lineIndexRight++, null);
561                    }
562                   
563                    leftHandTraversalIndex++;
564                }
565               
566                linesLeft.add(lineIndexLeft++, "-------------------------------------");
567                linesRight.add(lineIndexRight++, "-------------------------------------");
568            }
569            else if ((delta != null) && (delta.getType() == Delta.TYPE.INSERT)) {
570                for (int j = 0; j < delta.getRevised().size(); j++) {
571                    while (linesRight.get(lineIndexRight) != null) {
572                        lineIndexRight++;
573                    }
574                   
575                    linesRight.remove(lineIndexRight);
576                   
577                    while (lineIndexLeft < lineIndexRight) {
578                        linesLeft.add(lineIndexLeft++, null);
579                    }
580                }
581               
582                // ensure that what is equal is harmonized too (because after an insert always
583                // comes something equal). The traversal index will be updated automatically,
584                // too.
585                delta = null;
586               
587                linesLeft.add(lineIndexLeft++, "-------------------------------------");
588                linesRight.add(lineIndexRight++, "-------------------------------------");
589            }
590           
591            if ((delta == null) || (delta.getType() == Delta.TYPE.CHANGE)) {
592                int chunkSizeLeft = delta != null ? delta.getOriginal().size() : 1;
593                int chunkSizeRight = delta != null ? delta.getRevised().size() : 1;
594               
595                int chunksHandledLeft = 0;
596                int chunksHandledRight = 0;
597                   
598                for (int j = 0; j < Math.max(chunkSizeLeft, chunkSizeRight); j++) {
599                    if (chunksHandledLeft < chunkSizeLeft) {
600                        while (linesLeft.get(lineIndexLeft) != null) {
601                            lineIndexLeft++;
602                        }
603                       
604                        linesLeft.remove(lineIndexLeft);
605                        chunksHandledLeft++;
606                    }
607                       
608                    if (chunksHandledRight < chunkSizeRight) {
609                        while (linesRight.get(lineIndexRight) != null) {
610                            lineIndexRight++;
611                        }
612                   
613                        linesRight.remove(lineIndexRight);
614                        chunksHandledRight++;
615                    }
616                   
617                    while (lineIndexLeft < lineIndexRight) {
618                        linesLeft.add(lineIndexLeft++, null);
619                    }
620                    while (lineIndexRight < lineIndexLeft) {
621                        linesRight.add(lineIndexRight++, null);
622                    }
623                }
624               
625                linesLeft.add(lineIndexLeft++, "-------------------------------------");
626                linesRight.add(lineIndexRight++, "-------------------------------------");
627
628                leftHandTraversalIndex += delta != null ? delta.getOriginal().size() : 1;
629            }
630           
631            /*System.out.println(delta + "  " + leftHandTraversalIndex);
632            System.out.println("\n#################################################");
633            for (int j = 0; ((j < linesLeft.size()) || (j < linesRight.size())); j++) {
634                String left = j < linesLeft.size() ? linesLeft.get(j) : "";
635                String right = j < linesRight.size() ? linesRight.get(j) : "";
636               
637                StringBuffer line = new StringBuffer();
638                line.append(j);
639                line.append(":\t");
640                line.append(left);
641               
642                for (int k = line.length(); k < 60; k++) {
643                    line.append(' ');
644                }
645               
646                line.append(right);
647                System.out.println(line);
648            }*/
649        }
650       
651        int indentLeft = 0;
652        int indentRight = 0;
653       
654        for (int i = 0; i < linesLeft.size(); i++) {
655            StringBuffer line = new StringBuffer();
656            String left = linesLeft.get(i);
657            String right = linesRight.get(i);
658           
659            if (left != null) {
660                if (left.indexOf('}') >= 0) {
661                    indentLeft -= 2;
662                }
663
664                for (int j = 0; j < indentLeft; j++) {
665                    line.append(' ');
666                }
667                       
668                if (left.indexOf('{') >= 0) {
669                    indentLeft += 2;
670                }
671
672                line.append(left);
673            }
674
675            if (right != null) {
676                for (int j = line.length(); j < 100; j++) {
677                    line.append(' ');
678                }
679                if (right.indexOf('}') >= 0) {
680                    indentRight -= 2;
681                }
682
683                for (int j = 0; j < indentRight; j++) {
684                    line.append(' ');
685                }
686
687                if (right.indexOf('{') >= 0) {
688                    indentRight += 2;
689                }
690
691                line.append(right);
692            }
693
694            out.println(line);
695        }
696    }
697
698    /**
699     *
700     */
701    private static Set<ITask> getCommonInvolvedTasks(ITask task1, ITask task2) {
702        Set<ITask> result = getAllInvolvedTasks(task1);
703        result.retainAll(getAllInvolvedTasks(task2));
704        return result;
705    }
706
707    /**
708     *
709     */
710    private static Set<ITask> getAllInvolvedTasks(ITask task) {
711        final Set<ITask> result = new HashSet<ITask>();
712
713        task.accept(new DefaultTaskTraversingVisitor() {
714            @Override
715            public void visit(IEventTask eventTask) {
716                result.add(eventTask);
717            }
718
719            @Override
720            public void visit(IStructuringTemporalRelationship relationship) {
721                result.add(relationship);
722                super.visit(relationship);
723            }
724
725            @Override
726            public void visit(IMarkingTemporalRelationship relationship) {
727                result.add(relationship);
728                super.visit(relationship);
729            }
730        });
731
732        return result;
733    }
734
735    /**
736     *
737     */
738    private void dumpToLineList(TaskPath      path,
739                                TaskTraversal traversal,
740                                List<String>  lines)
741    {
742        boolean isTraversalElement = false;
743       
744        for (TaskPath candidate : traversal.getTraversalPaths()) {
745            if (path.equals(candidate)) {
746                isTraversalElement = true;
747                break;
748            }
749        }
750       
751        if (isTraversalElement) {
752            lines.add(path.getLast().toString());
753            lines.add(null);
754            /*ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
755            PrintStream out = new PrintStream(byteStream);
756
757            new TaskTreeEncoder().encode(path.getLast(), out);
758           
759            out.close();
760           
761            BufferedReader reader = new BufferedReader
762                (new InputStreamReader(new ByteArrayInputStream(byteStream.toByteArray())));
763           
764            String line = null;
765            do {
766                try {
767                    line = reader.readLine();
768                }
769                catch (IOException e) {
770                    // should not happen so dump hard
771                    e.printStackTrace();
772                }
773                lines.add(line != null ? line.trim() : null);
774            }
775            while (line != null);*/
776           
777        }
778        else {
779            ITask task = path.getLast();
780            if (task instanceof ITemporalRelationship) {
781                lines.add(task + " {");
782
783                if (task instanceof IStructuringTemporalRelationship) {
784                    List<ITask> children =
785                        ((IStructuringTemporalRelationship) task).getChildren();
786                   
787                    for (int i = 0; i < children.size(); i++) {
788                        path.add(children.get(i), i);
789                        dumpToLineList(path, traversal, lines);
790                        path.removeLast();
791                    }
792                }
793                else if (task instanceof IMarkingTemporalRelationship) {
794                    IMarkingTemporalRelationship relationship =
795                            (IMarkingTemporalRelationship) task;
796
797                    path.add(relationship.getMarkedTask(), 0);
798                    dumpToLineList(path, traversal, lines);
799                    path.removeLast();
800                }
801
802                if (lines.get(lines.size() - 1) != null) {
803                    lines.add("}");
804                }
805                else {
806                    lines.add(lines.size() - 1, "}");
807                }
808            }
809            else {
810                lines.add(task + " {}");
811            }
812        }
813    }
814
815    /**
816     *
817     */
818    /*@SuppressWarnings("unchecked")
819    private void getSelections(Delta delta, List<TaskPath> result) {
820        TaskPath path1 = getCommonPath((List<TaskPath>) delta.getOriginal().getLines());
821        TaskPath path2 = getCommonPath((List<TaskPath>) delta.getRevised().getLines());
822       
823        for (int i = 0; i < path1.size(); i++) {
824            if (path1.getTask(i) instanceof ISelection) {
825                System.out.println("found selection to ignore");
826                System.out.println(path1.subPath(0, i + 1));
827                result.add(path1.subPath(0, i + 1));
828                break;
829            }
830        }
831       
832        for (int i = 0; i < path2.size(); i++) {
833            if (path2.getTask(i) instanceof ISelection) {
834                System.out.println("found selection to ignore");
835                System.out.println(path2.subPath(0, i + 1));
836                result.add(path2.subPath(0, i + 1));
837                break;
838            }
839        }
840    }*/
841
842    /**
843     *
844     */
845    @SuppressWarnings("unchecked")
846    private void getPathsToRealIntersectingIterations(Delta delta, List<TaskPath> paths) {
847        TaskPath path1 = getCommonPath((List<TaskPath>) delta.getOriginal().getLines());
848        TaskPath path2 = getCommonPath((List<TaskPath>) delta.getRevised().getLines());
849       
850        // both paths denote those parent tasks that contain the delta.
851        // But there may be parent tasks still contained in the path that are iterations and
852        // that are disjoint with a parent in the other path. Those iterations need to be
853        // considered, as otherwise a created selection would be against the associative law of
854        // iterations. Hence, we search for the topmost iteration on both paths that is disjoint
855        // with a parent node in the respective other path.
856       
857        for (int i = 0; i < path1.size(); i++) {
858            TaskPath path1Prefix = path1.subPath(0, i + 1);
859            int[] idxsPath1 = leftHandSideTraversal.getIndexesRootedBy(path1Prefix);
860           
861            // normalize the indexes to be 0 the index of the delta. Everything left of it
862            // is smaller 0, everything right of it, larger.
863            idxsPath1[0] -= delta.getOriginal().getPosition();
864            idxsPath1[1] -= delta.getOriginal().getPosition() + delta.getOriginal().size() - 1;
865           
866            for (int j = 0; j < path2.size(); j++) {
867                TaskPath path2Prefix = path2.subPath(0, j + 1);
868                int[] idxsPath2 = rightHandSideTraversal.getIndexesRootedBy(path2Prefix);
869               
870                // normalize the indexes to be 0 the index of the delta. Everything left of it
871                // is smaller 0, everything right of it, larger.
872                idxsPath2[0] -= delta.getRevised().getPosition();
873                idxsPath2[1] -=
874                    delta.getRevised().getPosition() + delta.getRevised().size() - 1;
875               
876                // now compare the indexes and check, if the sublists are real subsets of each
877                // other. If not, they only have a real common subset but there is at least one
878                // non shared element on both sides. If so, and one is an iteration, we
879                // have to use it as elements to be selected
880                if (((idxsPath1[0] < idxsPath2[0]) && (idxsPath1[1] < idxsPath2[1])) ||
881                    ((idxsPath1[0] > idxsPath2[0]) && (idxsPath1[1] > idxsPath2[1])))
882                {
883                    if ((path1.get(i).getTask() instanceof IIteration) &&
884                        (path2.get(j).getTask() instanceof IIteration))
885                    {
886                        // System.out.println("found paths to real intersecting parents");
887                        // int[] indexBuf = leftHandSideTraversal.getIndexesRootedBy(path1Prefix);
888                        // System.out.println(idxsPath1[0] + "(" + indexBuf[0] + ")  " + idxsPath1[1] +
889                        //                    "(" + indexBuf[1] + ")  " + path1Prefix);
890                        // indexBuf = rightHandSideTraversal.getIndexesRootedBy(path2Prefix);
891                        // System.out.println(idxsPath2[0] + "(" + indexBuf[0] + ")  " + idxsPath2[1] +
892                        //                    "(" + indexBuf[1] + ")  " + path2Prefix);
893                       
894                        paths.add(path1Prefix);
895                        paths.add(path2Prefix);
896                        return;
897                    }
898                }
899            }
900        }
901    }
902
903    /**
904     *
905     */
906    private TaskPath getCommonPath(List<TaskPath> paths) {
907        TaskPath commonPath;
908       
909        if (paths.size() > 0) {
910            commonPath = new TaskPath(paths.get(0));
911           
912            for (int i = 1; i < paths.size(); i++) {
913                TaskPath comparePath = paths.get(i);
914                for (int j = 0; (j < commonPath.size()) && (j < comparePath.size()); j++) {
915                    if (!commonPath.get(j).equals(comparePath.get(j))) {
916                        while (commonPath.size() > j) {
917                            commonPath.removeLast();
918                        }
919                        break;
920                    }
921                }
922            }
923        }
924        else {
925            commonPath = new TaskPath();
926        }
927       
928        return commonPath;
929    }
930
931    /**
932     *
933     */
934    private void getNotFullyInterleavingIteration(Delta delta, List<TaskPath> result) {
935        getNotFullyInterleavingIterations(delta.getOriginal(), leftHandSideTraversal, result);
936        getNotFullyInterleavingIterations(delta.getRevised(), rightHandSideTraversal, result);
937    }
938   
939    /**
940     * If a chunk has a minimum number of 2 entries and at least one of them belongs to an iteration
941     * the does not cover the other and also expands to preceding or succeeding paths in the
942     * traversal, we can no merge the situation.
943     */
944    private void getNotFullyInterleavingIterations(Chunk          chunk,
945                                                   TaskTraversal  traversal,
946                                                   List<TaskPath> result)
947    {
948        @SuppressWarnings("unchecked")
949        List<TaskPath> paths = (List<TaskPath>) chunk.getLines();
950       
951        TaskPath commonPath = getCommonPath(paths);
952
953        if (paths.size() > 1) {
954            TaskPath firstPath = paths.get(0);
955            TaskPath lastPath = paths.get(paths.size() - 1);
956           
957            for (int i = commonPath.size(); i < firstPath.size(); i++) {
958                if (firstPath.get(i).getTask() instanceof IIteration) {
959                    // check, if the iteration covers things preceding the delta but not the whole
960                    // delta
961                    int[] indexes = traversal.getIndexesRootedBy(firstPath.subPath(0, i + 1));
962                   
963                    if ((indexes[0] < chunk.getPosition()) && // starts before the delta
964                        (indexes[1] < (chunk.getPosition() + chunk.size() - 1))) // does not cover delta
965                    {
966                        result.add(firstPath.subPath(0, i + 1));
967                        break;
968                    }
969                }
970            }
971
972            for (int i = commonPath.size(); i < lastPath.size(); i++) {
973                if (lastPath.get(i).getTask() instanceof IIteration) {
974                    // check, if the iteration covers things preceding the delta but not the whole
975                    // delta
976                    int[] indexes = traversal.getIndexesRootedBy(lastPath.subPath(0, i + 1));
977                   
978                    if ((indexes[0] > chunk.getPosition()) && // starts in between the delta
979                        (indexes[1] >= (chunk.getPosition() + chunk.size()))) // ends after the delta
980                    {
981                        result.add(lastPath.subPath(0, i + 1));
982                        break;
983                    }
984                }
985            }
986        }
987    }
988
989    /**
990     *
991     */
992    private void getPathsToParentTasksFullyInsideDelta(Delta delta, List<TaskPath> result) {
993        getPathsToParentTasksFullyInsideChunk(delta.getOriginal(), leftHandSideTraversal, result);
994        getPathsToParentTasksFullyInsideChunk(delta.getRevised(), rightHandSideTraversal, result);
995    }
996   
997    /**
998     * If a chunk has a minimum number of 2 entries and at least one of them belongs to an iteration
999     * the does not cover the other and also expands to preceding or succeeding paths in the
1000     * traversal, we can no merge the situation.
1001     */
1002    private void getPathsToParentTasksFullyInsideChunk(Chunk          chunk,
1003                                                       TaskTraversal  traversal,
1004                                                       List<TaskPath> result)
1005    {
1006        @SuppressWarnings("unchecked")
1007        List<TaskPath> paths = (List<TaskPath>) chunk.getLines();
1008       
1009        TaskPath commonPath = getCommonPath(paths);
1010
1011        for (TaskPath path : paths) {
1012            for (int i = commonPath.size(); i < path.size(); i++) {
1013                if (!(path.getLast() instanceof IEventTask)) {
1014                    // check, if the parent task covers things fully inside the delta
1015                    int[] indexes = traversal.getIndexesRootedBy(path.subPath(0, i + 1));
1016                   
1017                    if ((chunk.getPosition() <= indexes[0]) && // starts in the delta
1018                        (indexes[1] < (chunk.getPosition() + chunk.size()))) // ends in the delta
1019                    {
1020                        // System.out.println("found path to parent task lieing completely in the " +
1021                        //                    "delta: " + path.subPath(0, i + 1));
1022                        result.add(path.subPath(0, i + 1));
1023                        break;
1024                    }
1025                }
1026            }
1027        }
1028    }
1029
1030    /**
1031     *
1032     */
1033    private void getParentOptionals(TaskPath taskPath, List<TaskPath> result) {
1034        ITask task = taskPath.getLast();
1035       
1036        if (task instanceof IOptional) {
1037            result.add(new TaskPath(taskPath));
1038        }
1039        else if (task instanceof ISequence) {
1040            for (int i = 0; i < ((ISequence) task).getChildren().size(); i++) {
1041                taskPath.add(((ISequence) task).getChildren().get(i), i);
1042                getParentOptionals(taskPath, result);
1043                taskPath.removeLast();
1044            }
1045        }
1046        else if (task instanceof ISelection) {
1047            for (int i = 0; i < ((ISelection) task).getChildren().size(); i++) {
1048                taskPath.add(((ISelection) task).getChildren().get(i), 0);
1049                getParentOptionals(taskPath, result);
1050                taskPath.removeLast();
1051            }
1052        }
1053        else if (task instanceof IIteration) {
1054            taskPath.add(((IIteration) task).getMarkedTask(), 0);
1055            getParentOptionals(taskPath, result);
1056            taskPath.removeLast();
1057        }
1058    }
1059
1060    /**
1061     *
1062     */
1063    @SuppressWarnings("unchecked")
1064    private int getDiffLevel(Patch         patch,
1065                             TaskTraversal traversal1,
1066                             TaskTraversal traversal2)
1067    {
1068        int diffCount = 0;
1069        int allCount = 0;
1070       
1071        for (Delta delta : patch.getDeltas()) {
1072            for (TaskPath path : (List<TaskPath>) delta.getOriginal().getLines()) {
1073                // inefficient actions are counted later.
1074                if (!isInefficientAction(path.getLast())) {
1075                    diffCount += getDiffPenalty(path.getLast());
1076                }
1077            }
1078            for (TaskPath path : (List<TaskPath>) delta.getRevised().getLines()) {
1079                // inefficient actions are counted later.
1080                if (!isInefficientAction(path.getLast())) {
1081                    diffCount += getDiffPenalty(path.getLast());
1082                }
1083            }
1084        }
1085       
1086        //if (getPathsToRealIntersectingIterations().size() > 0) {
1087            // add a penalty if intersecting iterations are present
1088            //count += 10;
1089        //}
1090       
1091        // add a penalty for inefficient actions which are equal as they have to be treated
1092        // unequal, too. Otherwise, many scrolls, e.g., would pretend a high equality of tasks
1093        // which contain only some non inefficient actions
1094        for (TaskPath path : traversal1.getTraversalPaths()) {
1095            if (isInefficientAction(path.getLast())) {
1096                diffCount++;
1097                allCount++;
1098            }
1099            else {
1100                allCount += getDiffPenalty(path.getLast());
1101            }
1102        }
1103       
1104        for (TaskPath path : traversal2.getTraversalPaths()) {
1105            if (isInefficientAction(path.getLast())) {
1106                diffCount++;
1107                allCount++;
1108            }
1109            else {
1110                allCount += getDiffPenalty(path.getLast());
1111            }
1112        }
1113       
1114        if (allCount == 0) {
1115            // this happens, if all actions are inefficient. Return the highest diff level
1116            return 100;
1117        }
1118        else {
1119            return (100 * diffCount) / allCount;
1120        }
1121    }
1122
1123    /**
1124     *
1125     */
1126    private boolean isInefficientAction(ITask task) {
1127        ITaskInstance inst = task.getInstances().iterator().next();
1128       
1129        if ((inst instanceof IEventTaskInstance) &&
1130            (((IEventTaskInstance) inst).getEvent().getType() instanceof Scroll))
1131        {
1132            return true;
1133        }
1134        else if (task instanceof IMarkingTemporalRelationship) {
1135            return isInefficientAction(((IMarkingTemporalRelationship) task).getMarkedTask());
1136        }
1137        else {
1138            return false;
1139        }
1140    }
1141
1142
1143    /**
1144     *
1145     */
1146    private int getDiffPenalty(ITask task) {
1147        if (task instanceof IEventTask) {
1148            return 1;
1149        }
1150        else if (task instanceof IMarkingTemporalRelationship) {
1151            return getDiffPenalty(((IMarkingTemporalRelationship) task).getMarkedTask());
1152        }
1153        else if (task instanceof IStructuringTemporalRelationship) {
1154            int result = 0;
1155           
1156            for (ITask child : ((IStructuringTemporalRelationship) task).getChildren()) {
1157                result += getDiffPenalty(child);
1158            }
1159           
1160            if (task instanceof ISelection) {
1161                result /= ((IStructuringTemporalRelationship) task).getChildren().size();
1162            }
1163           
1164            return result;
1165        }
1166       
1167        throw new IllegalArgumentException("unknown type of task: " + task);
1168    }
1169}
Note: See TracBrowser for help on using the repository browser.