source: branches/ralph/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/SequenceForTaskDetectionRuleAlignment.java @ 1645

Last change on this file since 1645 was 1645, checked in by rkrimmel, 10 years ago

Trying to replace event instances in the sessions

File size: 28.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;
16
17import java.util.ArrayList;
18import java.util.Collections;
19import java.util.Comparator;
20import java.util.HashMap;
21import java.util.HashSet;
22import java.util.Iterator;
23import java.util.LinkedList;
24import java.util.List;
25import java.util.Map;
26import java.util.Set;
27import java.util.logging.Level;
28
29import de.ugoe.cs.autoquest.tasktrees.alignment.algorithms.Match;
30import de.ugoe.cs.autoquest.tasktrees.alignment.algorithms.MatchOccurence;
31import de.ugoe.cs.autoquest.tasktrees.alignment.algorithms.NumberSequence;
32import de.ugoe.cs.autoquest.tasktrees.alignment.matrix.PairwiseAlignmentGenerator;
33import de.ugoe.cs.autoquest.tasktrees.alignment.matrix.PairwiseAlignmentStorage;
34import de.ugoe.cs.autoquest.tasktrees.alignment.matrix.ObjectDistanceSubstitionMatrix;
35import de.ugoe.cs.autoquest.tasktrees.taskequality.TaskEquality;
36import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
37import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance;
38import de.ugoe.cs.autoquest.tasktrees.treeifc.IOptional;
39import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
40import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance;
41import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
42import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance;
43import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
44import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskBuilder;
45import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskFactory;
46import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
47import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstanceList;
48import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
49import de.ugoe.cs.autoquest.usageprofiles.SymbolMap;
50import de.ugoe.cs.util.StopWatch;
51import de.ugoe.cs.util.console.Console;
52
53/**
54 * <p>
55 * This class implements the major rule for creating task trees based on a set
56 * of recorded user sessions. For this, it first harmonizes all tasks. This
57 * eases later comparison. Then it searches the sessions for iterations and
58 * replaces them accordingly. Then it searches for sub sequences being the
59 * longest and occurring most often. For each found sub sequence, it replaces
60 * the occurrences by creating appropriate {@link ISequence}s. Afterwards, again
61 * searches for iterations and then again for sub sequences until no more
62 * replacements are done.
63 * </p>
64 * <p>
65 *
66 *
67 * @author Patrick Harms
68 */
69class SequenceForTaskDetectionRuleAlignment implements ISessionScopeRule {
70
71        /**
72         * <p>
73         * the task factory to be used for creating substructures for the temporal
74         * relationships identified during rul application
75         * </p>
76         */
77        private ITaskFactory taskFactory;
78        /**
79         * <p>
80         * the task builder to be used for creating substructures for the temporal
81         * relationships identified during rule application
82         * </p>
83         */
84        private ITaskBuilder taskBuilder;
85
86        /**
87         * <p>
88         * the task handling strategy to be used for comparing tasks for
89         * preparation, i.e., before the tasks are harmonized
90         * </p>
91         */
92        private TaskHandlingStrategy preparationTaskHandlingStrategy;
93
94        /**
95         * <p>
96         * the task handling strategy to be used for comparing tasks during
97         * iteration detection i.e., after the tasks are
98         * harmonized
99         * </p>
100         */
101        private TaskHandlingStrategy identityTaskHandlingStrategy;
102
103
104
105        /**
106         * <p>
107         * instantiates the rule and initializes it with a task equality to be
108         * considered when comparing tasks as well as a task factory and builder to
109         * be used for creating task structures.
110         * </p>
111         *
112         * @param minimalTaskEquality
113         *            the task equality to be considered when comparing tasks
114         * @param taskFactory
115         *            the task factory to be used for creating substructures
116         * @param taskBuilder
117         *            the task builder to be used for creating substructures
118         */
119
120        SequenceForTaskDetectionRuleAlignment(TaskEquality minimalTaskEquality,
121                        ITaskFactory taskFactory, ITaskBuilder taskBuilder) {
122                this.taskFactory = taskFactory;
123                this.taskBuilder = taskBuilder;
124
125                this.preparationTaskHandlingStrategy = new TaskHandlingStrategy(
126                                minimalTaskEquality);
127                this.identityTaskHandlingStrategy = new TaskHandlingStrategy(
128                                TaskEquality.IDENTICAL);
129
130        }
131
132        /*
133         * (non-Javadoc)
134         *
135         * @see java.lang.Object#toString()
136         */
137        @Override
138        public String toString() {
139                return "SequenceForTaskDetectionRuleAlignment";
140        }
141
142        /*
143         * (non-Javadoc)
144         *
145         * @see
146         * de.ugoe.cs.autoquest.tasktrees.temporalrelation.ISessionScopeRule#apply
147         * (java.util.List)
148         */
149        @Override
150        public RuleApplicationResult apply(List<IUserSession> sessions) {
151                RuleApplicationData appData = new RuleApplicationData(sessions);
152
153                // this is the real rule application. Loop while something is replaced.
154                SymbolMap<ITaskInstance, ITask> uniqueTasks = harmonizeEventTaskInstancesModel(appData);
155
156                // Generate a substitution matrix between all occurring events.
157                Console.traceln(Level.INFO, "generating substitution matrix");
158                ObjectDistanceSubstitionMatrix submat = new ObjectDistanceSubstitionMatrix(
159                                uniqueTasks, 6, -3);
160                submat.generate();
161
162                // Generate pairwise alignments
163                Console.traceln(Level.INFO, "generating pairwise alignments");
164                LinkedList<Match> matchseqs = new LinkedList<Match>();
165                PairwiseAlignmentStorage alignments = PairwiseAlignmentGenerator
166                                .generate(appData.getNumberSequences(), submat, 9);
167
168                // Retrieve all matches reached a specific threshold
169                Console.traceln(Level.INFO, "retrieving significant sequence pieces");
170                for (int i = 0; i < appData.getNumberSequences().size(); i++) {
171                        Console.traceln(
172                                        Level.FINEST,
173                                        "retrieving significant sequence pieces:  "
174                                                        + Math.round((float) i / (float) appData.getNumberSequences().size()
175                                                                        * 100) + "%");
176                        for (int j = 0; j < appData.getNumberSequences().size(); j++) {
177                                if (i != j) {
178                                        matchseqs.addAll(alignments.get(i, j).getMatches());
179                                }
180                        }
181                }
182                Console.traceln(Level.FINEST,
183                                "retrieving significant sequence pieces:  100%");
184                Console.traceln(Level.INFO, "searching for patterns occuring most");
185
186                // search each match in every other sequence
187                for (Iterator<Match> it = matchseqs.iterator(); it.hasNext();) {
188                        Match pattern = it.next();
189
190                        // Skip sequences with more 0 events (scrolls) than other events.
191                        // Both of the pattern sequences are equally long, so the zero
192                        // counts just need to be smaller than the length of one sequence
193                        if (pattern.getFirstSequence().eventCount(0)
194                                        + pattern.getSecondSequence().eventCount(0) + 1 > pattern
195                                        .getFirstSequence().size())
196                                continue;
197       
198                        for (int j = 0; j < appData.getNumberSequences().size(); j++) {
199                                LinkedList<Integer> startpositions = appData.getNumberSequences().get(j)
200                                                .containsPattern(pattern);
201                                if (startpositions.size() > 0) {
202                                       
203                                        for (Iterator<Integer> jt = startpositions.iterator(); jt
204                                                        .hasNext();) {
205                                                pattern.addOccurence(
206                                                                new MatchOccurence(jt.next(), j));
207                                        }
208
209                                }
210                        }
211                }
212
213                Console.traceln(Level.INFO, "sorting results");
214                // Sort results to get the most occurring results
215                Comparator<Match> comparator = new Comparator<Match>() {
216                        public int compare(Match m1, Match m2) {
217                                return m2.occurenceCount() - m1.occurenceCount(); // use your
218                                                                                                                                        // logic
219                        }
220                };
221                Collections.sort(matchseqs, comparator);
222               
223
224                // TODO: Harmonize matches: finding double matches and merge them
225                /*
226                Console.traceln(Level.INFO, "harmonizing matches");
227                int i=0;
228               
229                while(i<matchseqs.size()) {
230                        int j=i;
231                        while(j<matchseqs.size()) {
232                                if(i!=j) {
233                                        if(matchseqs.get(i).equals(matchseqs.get(j))) {
234                                                //matchseqs.get(i).addOccurencesOf(matchseqs.get(j));
235                                                matchseqs.remove(j);
236                                       
237                                                //System.out.println("Sequence " + i);
238                                                //matchseqs.get(i).getFirstSequence().printSequence();
239                                                //matchseqs.get(i).getSecondSequence().printSequence();
240                                                //System.out.println("is equal to sequence " + j);
241                                                //matchseqs.get(j).getFirstSequence().printSequence();
242                                                //matchseqs.get(j).getSecondSequence().printSequence();
243                                                continue;
244                                        }
245                                }
246                                j++;
247                        }
248                        i++;
249                }
250                Collections.sort(matchseqs, comparator);
251                */
252               
253               
254                // Just printing the results out
255                for (int i = 0; i < matchseqs.size(); i++) {
256                        // Every pattern consists of 2 sequences, therefore the minimum
257                        // occurrences here is 2.
258                        // We just need the sequences also occurring in other sequences as
259                        // well
260                        if (matchseqs.get(i).occurenceCount() > 2) {
261                               
262                                ISequence task = matchAsSequence(appData,matchseqs.get(i));
263                                for(Iterator<MatchOccurence> it = matchseqs.get(i).getOccurences().iterator();it.hasNext();) {
264                                        MatchOccurence oc = it.next();
265                                        System.out.println("Trying to replace sequence: ");
266                                        matchseqs.get(i).getFirstSequence().printSequence();
267                                        matchseqs.get(i).getSecondSequence().printSequence();
268                                        System.out.println(" in session number: " + (oc.getSequenceId()+1) + " at position " + oc.getStartindex() + "-" + (oc.getStartindex()+matchseqs.get(i).getFirstSequence().size()));
269                                        System.out.println("Printing session: ");
270                                        for(int j=0;j<sessions.get(oc.getSequenceId()).size();j++) {
271                                                System.out.println(j+ ": " + sessions.get(oc.getSequenceId()).get(j));
272                                        }
273                                        List<ISequenceInstance> sequenceInstances = new LinkedList<ISequenceInstance>();
274                                        RuleUtils.createNewSubSequenceInRange(sessions.get(oc.getSequenceId()), oc.getStartindex(), oc.getStartindex()+matchseqs.get(i).getFirstSequence().size()-1, task,      taskFactory, taskBuilder);
275        }
276                                System.out.println(task);
277                                //matchseqs.get(i).getFirstSequence().printSequence();
278                                //matchseqs.get(i).getSecondSequence().printSequence();
279                                //System.out.println("Found pattern "
280                                //              + matchseqs.get(i).occurenceCount() + " times");
281                                System.out.println();
282                        }
283                }
284               
285
286                alignments = null;
287
288                do {
289                 
290                  appData.getStopWatch().start("whole loop"); //
291                  detectAndReplaceIterations(appData);
292                 
293                  appData.getStopWatch().start("task replacement"); //
294                  //detectAndReplaceTasks(appData); //
295                  appData.getStopWatch().stop("task replacement"); //
296                  appData.getStopWatch().stop("whole loop");
297                 
298                  appData.getStopWatch().dumpStatistics(System.out); //
299                  appData.getStopWatch().reset();
300                 
301                  } while (appData.detectedAndReplacedTasks());
302                 
303
304                Console.println("created "
305                                + appData.getResult().getNewlyCreatedTasks().size()
306                                + " new tasks and "
307                                + appData.getResult().getNewlyCreatedTaskInstances().size()
308                                + " appropriate instances\n");
309
310                if ((appData.getResult().getNewlyCreatedTasks().size() > 0)
311                                || (appData.getResult().getNewlyCreatedTaskInstances().size() > 0)) {
312                        appData.getResult().setRuleApplicationStatus(
313                                        RuleApplicationStatus.FINISHED);
314                }
315
316                return appData.getResult();
317        }
318
319       
320       
321        /**
322         * <p>
323         * harmonizes the event task instances by unifying tasks. This is done, as
324         * initially the event tasks being equal with respect to the considered task
325         * equality are distinct objects. The comparison of these distinct objects
326         * is more time consuming than comparing the object references.
327         * </p>
328         *
329         * @param appData
330         *            the rule application data combining all data used for applying
331         *            this rule
332         * @return Returns the unique tasks symbol map
333         */
334        private SymbolMap<ITaskInstance, ITask> harmonizeEventTaskInstancesModel(
335                        RuleApplicationData appData) {
336                Console.traceln(Level.INFO,
337                                "harmonizing task model of event task instances");
338                appData.getStopWatch().start("harmonizing event tasks");
339
340                SymbolMap<ITaskInstance, ITask> uniqueTasks = preparationTaskHandlingStrategy
341                                .createSymbolMap();
342                TaskInstanceComparator comparator = preparationTaskHandlingStrategy
343                                .getTaskComparator();
344
345                int unifiedTasks = 0;
346                ITask task;
347                List<IUserSession> sessions = appData.getSessions();
348                int sessionNo = 0;
349                for (IUserSession session : sessions) {
350                        Console.traceln(Level.FINE, "handling " + (++sessionNo) + ". "
351                                        + session);
352                        NumberSequence templist = new NumberSequence(session.size());
353
354                        for (int i = 0; i < session.size(); i++) {
355                                ITaskInstance taskInstance = session.get(i);
356                                task = uniqueTasks.getValue(taskInstance);
357
358                                if (task == null) {
359                                        uniqueTasks.addSymbol(taskInstance, taskInstance.getTask());
360                                        templist.getSequence()[i] = taskInstance.getTask().getId();
361               
362                                } else {
363                                        taskBuilder.setTask(taskInstance, task);
364                                        templist.getSequence()[i] = task.getId();
365                                        unifiedTasks++;
366                                }
367                                appData.getNumber2Task().put(templist.getSequence()[i], taskInstance.getTask());
368                        }
369                        //Each NumberSequence is identified by its id, beginning to count at zero
370                        templist.setId(appData.getNumberSequences().size());
371                        appData.getNumberSequences().add(templist);
372                        comparator.clearBuffers();
373                }
374
375                appData.getStopWatch().stop("harmonizing event tasks");
376                Console.traceln(Level.INFO, "harmonized " + unifiedTasks
377                                + " task occurrences (still " + uniqueTasks.size()
378                                + " different tasks)");
379
380                appData.getStopWatch().dumpStatistics(System.out);
381                appData.getStopWatch().reset();
382                return uniqueTasks;
383        }
384
385        /**
386         * <p>
387         * searches for direct iterations of single tasks in all sequences and
388         * replaces them with {@link IIteration}s, respectively appropriate
389         * instances. Also all single occurrences of a task that is iterated
390         * somewhen are replaced with iterations to have again an efficient way for
391         * task comparisons.
392         * </p>
393         *
394         * @param appData
395         *            the rule application data combining all data used for applying
396         *            this rule
397         */
398        private void detectAndReplaceIterations(RuleApplicationData appData) {
399                Console.traceln(Level.FINE, "detecting iterations");
400                appData.getStopWatch().start("detecting iterations");
401
402                List<IUserSession> sessions = appData.getSessions();
403
404                Set<ITask> iteratedTasks = searchIteratedTasks(sessions);
405
406                if (iteratedTasks.size() > 0) {
407                        replaceIterationsOf(iteratedTasks, sessions, appData);
408                }
409
410                appData.getStopWatch().stop("detecting iterations");
411                Console.traceln(Level.INFO, "replaced " + iteratedTasks.size()
412                                + " iterated tasks");
413        }
414
415        /**
416         * <p>
417         * searches the provided sessions for task iterations. If a task is
418         * iterated, it is added to the returned set.
419         * </p>
420         *
421         * @param the
422         *            session to search for iterations in
423         *
424         * @return a set of tasks being iterated somewhere
425         */
426        private Set<ITask> searchIteratedTasks(List<IUserSession> sessions) {
427                Set<ITask> iteratedTasks = new HashSet<ITask>();
428                for (IUserSession session : sessions) {
429                        for (int i = 0; i < (session.size() - 1); i++) {
430                                // we prepared the task instances to refer to unique tasks, if
431                                // they are treated
432                                // as equal. Therefore, we just compare the identity of the
433                                // tasks of the task
434                                // instances
435                                if (session.get(i).getTask() == session.get(i + 1).getTask()) {
436                                        iteratedTasks.add(session.get(i).getTask());
437                                }
438                        }
439                }
440
441                return iteratedTasks;
442        }
443
444        /**
445         * <p>
446         * replaces all occurrences of all tasks provided in the set with iterations
447         * </p>
448         *
449         * @param iteratedTasks
450         *            the tasks to be replaced with iterations
451         * @param sessions
452         *            the sessions in which the tasks are to be replaced
453         * @param appData
454         *            the rule application data combining all data used for applying
455         *            this rule
456         */
457        private void replaceIterationsOf(Set<ITask> iteratedTasks,
458                        List<IUserSession> sessions, RuleApplicationData appData) {
459                Map<ITask, IIteration> iterations = new HashMap<ITask, IIteration>();
460                Map<IIteration, List<IIterationInstance>> iterationInstances = new HashMap<IIteration, List<IIterationInstance>>();
461
462                for (ITask iteratedTask : iteratedTasks) {
463                        IIteration iteration = taskFactory.createNewIteration();
464                        iterations.put(iteratedTask, iteration);
465                        iterationInstances.put(iteration,
466                                        new LinkedList<IIterationInstance>());
467                }
468
469                IIterationInstance iterationInstance;
470
471                for (IUserSession session : sessions) {
472                        int index = 0;
473                        iterationInstance = null;
474
475                        while (index < session.size()) {
476                                // we prepared the task instances to refer to unique tasks, if
477                                // they are treated
478                                // as equal. Therefore, we just compare the identity of the
479                                // tasks of the task
480                                // instances
481                                ITask currentTask = session.get(index).getTask();
482                                IIteration iteration = iterations.get(currentTask);
483                                if (iteration != null) {
484                                        if ((iterationInstance == null)
485                                                        || (iterationInstance.getTask() != iteration)) {
486                                                iterationInstance = taskFactory
487                                                                .createNewTaskInstance(iteration);
488                                                iterationInstances.get(iteration)
489                                                                .add(iterationInstance);
490                                                taskBuilder.addTaskInstance(session, index,
491                                                                iterationInstance);
492                                                index++;
493                                        }
494
495                                        taskBuilder.addChild(iterationInstance, session.get(index));
496                                        taskBuilder.removeTaskInstance(session, index);
497                                } else {
498                                        if (iterationInstance != null) {
499                                                iterationInstance = null;
500                                        }
501                                        index++;
502                                }
503                        }
504                }
505
506                for (Map.Entry<IIteration, List<IIterationInstance>> entry : iterationInstances
507                                .entrySet()) {
508                        harmonizeIterationInstancesModel(entry.getKey(), entry.getValue());
509                }
510        }
511
512       
513         ISequence matchAsSequence(RuleApplicationData appData,Match m) {
514               
515                ISequence sequence = taskFactory.createNewSequence();
516               
517                int[] first = m.getFirstSequence().getSequence();
518                int[] second = m.getSecondSequence().getSequence();
519
520               
521                //Both sequences of a match are equally long
522                for(int i=0;i<m.getFirstSequence().size();i++) {
523       
524                        //Two gaps aligned to each other: Have not seen it happening so far, just to handle it
525                        if(first[i]==-1 && second[i]==-1) {
526                                //TODO: Do nothing?
527                        }
528                        //Both events are equal, we can simply add the task referring to the number
529                        else if(first[i]==second[i]) {
530                                taskBuilder.addChild(sequence, appData.getNumber2Task().get(first[i]));
531                        }
532                        //We have a gap in the first sequence, we need to add the task of the second sequence as optional
533                        else if(first[i]==-1 && second[i]!=-1) {
534                                IOptional optional = taskFactory.createNewOptional();
535                                taskBuilder.setMarkedTask(optional, appData.getNumber2Task().get(second[i]));
536                                taskBuilder.addChild(sequence,optional);
537                        }
538                        //We have a gap in the second sequence, we need to add the task of the first sequence as optional
539                        else if(first[i]!=-1 && second[i]==-1) {
540                                IOptional optional = taskFactory.createNewOptional();
541                                taskBuilder.setMarkedTask(optional, appData.getNumber2Task().get(first[i]));
542                                taskBuilder.addChild(sequence,optional);
543                        }
544                        //Both tasks are unequal, we need to insert a selection here
545                        else {
546                                ISelection selection = taskFactory.createNewSelection();
547                                taskBuilder.addChild(selection, appData.getNumber2Task().get(first[i]));
548                                taskBuilder.addChild(selection, appData.getNumber2Task().get(second[i]));
549                                taskBuilder.addChild(sequence,selection);
550                        }
551                }
552                        return sequence;
553                }
554       
555       
556        /**
557         * <p>
558         * TODO clarify why this is done
559         * </p>
560         */
561        private void harmonizeIterationInstancesModel(IIteration iteration,
562                        List<IIterationInstance> iterationInstances) {
563                List<ITask> iteratedTaskVariants = new LinkedList<ITask>();
564                TaskInstanceComparator comparator = preparationTaskHandlingStrategy
565                                .getTaskComparator();
566
567                // merge the lexically different variants of iterated task to a unique
568                // list
569                for (IIterationInstance iterationInstance : iterationInstances) {
570                        for (ITaskInstance executionVariant : iterationInstance) {
571                                ITask candidate = executionVariant.getTask();
572
573                                boolean found = false;
574                                for (ITask taskVariant : iteratedTaskVariants) {
575                                        if (comparator.areLexicallyEqual(taskVariant, candidate)) {
576                                                taskBuilder.setTask(executionVariant, taskVariant);
577                                                found = true;
578                                                break;
579                                        }
580                                }
581
582                                if (!found) {
583                                        iteratedTaskVariants.add(candidate);
584                                }
585                        }
586                }
587
588                // if there are more than one lexically different variant of iterated
589                // tasks, adapt the
590                // iteration model to be a selection of different variants. In this case
591                // also adapt
592                // the generated iteration instances to correctly contain selection
593                // instances. If there
594                // is only one variant of an iterated task, simply set this as the
595                // marked task of the
596                // iteration. In this case, the instances can be preserved as is
597                if (iteratedTaskVariants.size() > 1) {
598                        ISelection selection = taskFactory.createNewSelection();
599
600                        for (ITask variant : iteratedTaskVariants) {
601                                taskBuilder.addChild(selection, variant);
602                        }
603
604                        taskBuilder.setMarkedTask(iteration, selection);
605
606                        for (IIterationInstance instance : iterationInstances) {
607                                for (int i = 0; i < instance.size(); i++) {
608                                        ISelectionInstance selectionInstance = taskFactory
609                                                        .createNewTaskInstance(selection);
610                                        taskBuilder.setChild(selectionInstance, instance.get(i));
611                                        taskBuilder.setTaskInstance(instance, i, selectionInstance);
612                                }
613                        }
614                } else {
615                        taskBuilder.setMarkedTask(iteration, iteratedTaskVariants.get(0));
616                }
617        }
618
619        /**
620         * TODO go on commenting
621         *
622         * @param appData
623         *            the rule application data combining all data used for applying
624         *            this rule
625         */
626        private void detectAndReplaceTasks(RuleApplicationData appData) {
627                Console.traceln(Level.FINE, "detecting and replacing tasks");
628                appData.getStopWatch().start("detecting tasks");
629
630                //getSequencesOccuringMostOften(appData);
631
632                appData.getStopWatch().stop("detecting tasks");
633                appData.getStopWatch().start("replacing tasks");
634
635                replaceSequencesOccurringMostOften(appData);
636
637                appData.getStopWatch().stop("replacing tasks");
638               
639                //Console.traceln(Level.INFO, "detected and replaced "
640                //              + appData.getLastFoundTasks().size() + " tasks occuring "
641                //              + appData.getLastFoundTasks().getOccurrenceCount() + " times");
642        }
643
644       
645
646
647        /**
648         * @param appData
649         *            the rule application data combining all data used for applying
650         *            this rule
651         */
652        private void replaceSequencesOccurringMostOften(RuleApplicationData appData) {
653                appData.detectedAndReplacedTasks(false);
654
655        /*
656                        Console.traceln(Level.FINER, "replacing tasks occurrences");
657                       
658                        for (List<ITaskInstance> task : appData.getLastFoundTasks()) {
659                                ISequence sequence = taskFactory.createNewSequence();
660
661                                Console.traceln(Level.FINEST, "replacing " + sequence.getId()
662                                                + ": " + task);
663
664                                List<ISequenceInstance> sequenceInstances = replaceTaskOccurrences(
665                                                task, appData.getSessions(), sequence);
666
667                                harmonizeSequenceInstancesModel(sequence, sequenceInstances,
668                                                task.size());
669                                appData.detectedAndReplacedTasks(appData
670                                                .detectedAndReplacedTasks()
671                                                || (sequenceInstances.size() > 0));
672
673                                if (sequenceInstances.size() < appData.getLastFoundTasks()
674                                                .getOccurrenceCount()) {
675                                        Console.traceln(Level.FINE,
676                                                        sequence.getId()
677                                                                        + ": replaced task only "
678                                                                        + sequenceInstances.size()
679                                                                        + " times instead of expected "
680                                                                        + appData.getLastFoundTasks()
681                                                                                        .getOccurrenceCount());
682                                }
683                        }
684                        */
685        }
686
687
688        /**
689     *
690     */
691        private void harmonizeSequenceInstancesModel(ISequence sequence,
692                        List<ISequenceInstance> sequenceInstances, int sequenceLength) {
693                TaskInstanceComparator comparator = preparationTaskHandlingStrategy
694                                .getTaskComparator();
695
696                // ensure for each subtask that lexically different variants are
697                // preserved
698                for (int subTaskIndex = 0; subTaskIndex < sequenceLength; subTaskIndex++) {
699                        List<ITask> subTaskVariants = new LinkedList<ITask>();
700
701                        for (ISequenceInstance sequenceInstance : sequenceInstances) {
702                                ITask candidate = sequenceInstance.get(subTaskIndex).getTask();
703
704                                boolean found = false;
705
706                                for (int i = 0; i < subTaskVariants.size(); i++) {
707                                        if (comparator.areLexicallyEqual(subTaskVariants.get(i),
708                                                        candidate)) {
709                                                taskBuilder.setTask(sequenceInstance.get(subTaskIndex),
710                                                                subTaskVariants.get(i));
711
712                                                found = true;
713                                                break;
714                                        }
715                                }
716
717                                if (!found) {
718                                        subTaskVariants.add(candidate);
719                                }
720                        }
721
722                        // if there are more than one lexically different variant of the sub
723                        // task at
724                        // the considered position, adapt the sequence model at that
725                        // position to have
726                        // a selection of the different variants. In this case also adapt
727                        // the
728                        // generated sequence instances to correctly contain selection
729                        // instances. If
730                        // there is only one variant of sub tasks at the given position,
731                        // simply set
732                        // this variant as the sub task of the selection. In this case, the
733                        // instances
734                        // can be preserved as is
735                        if (subTaskVariants.size() > 1) {
736                                ISelection selection = taskFactory.createNewSelection();
737
738                                for (ITask variant : subTaskVariants) {
739                                        taskBuilder.addChild(selection, variant);
740                                }
741
742                                taskBuilder.addChild(sequence, selection);
743
744                                for (ISequenceInstance instance : sequenceInstances) {
745                                        ISelectionInstance selectionInstance = taskFactory
746                                                        .createNewTaskInstance(selection);
747                                        taskBuilder.setChild(selectionInstance,
748                                                        instance.get(subTaskIndex));
749                                        taskBuilder.setTaskInstance(instance, subTaskIndex,
750                                                        selectionInstance);
751                                }
752                        } else if (subTaskVariants.size() == 1) {
753                                taskBuilder.addChild(sequence, subTaskVariants.get(0));
754                        }
755                }
756        }
757
758        /**
759         * @param tree
760         */
761        private List<ISequenceInstance> replaceTaskOccurrences(
762                        List<ITaskInstance> task, List<IUserSession> sessions,
763                        ISequence temporalTaskModel) {
764                List<ISequenceInstance> sequenceInstances = new LinkedList<ISequenceInstance>();
765
766                for (IUserSession session : sessions) {
767                        int index = -1;
768
769                        do {
770                                index = getSubListIndex(session, task, ++index);
771
772                                if (index > -1) {
773                                        sequenceInstances.add(RuleUtils
774                                                        .createNewSubSequenceInRange(session, index, index
775                                                                        + task.size() - 1, temporalTaskModel,
776                                                                        taskFactory, taskBuilder));
777                                }
778                        } while (index > -1);
779                }
780
781                return sequenceInstances;
782        }
783
784        /**
785         * @param trie
786         * @param object
787         * @return
788         */
789        private int getSubListIndex(ITaskInstanceList list,
790                        List<ITaskInstance> subList, int startIndex) {
791                boolean matchFound;
792                int result = -1;
793
794                for (int i = startIndex; i <= list.size() - subList.size(); i++) {
795                        matchFound = true;
796
797                        for (int j = 0; j < subList.size(); j++) {
798                                // we prepared the task instances to refer to unique tasks, if
799                                // they are treated
800                                // as equal. Therefore, we just compare the identity of the
801                                // tasks of the task
802                                // instances
803                                if (list.get(i + j).getTask() != subList.get(j).getTask()) {
804                                        matchFound = false;
805                                        break;
806                                }
807                        }
808
809                        if (matchFound) {
810                                result = i;
811                                break;
812                        }
813                }
814
815                return result;
816        }
817
818
819        /**
820     *
821     */
822        private static class RuleApplicationData {
823
824               
825                private HashMap<Integer,ITask> number2task;
826               
827               
828                private ArrayList<NumberSequence> numberseqs;
829               
830                /**
831         *
832         */
833                private List<IUserSession> sessions;
834
835
836
837                /**
838         *
839         */
840                private boolean detectedAndReplacedTasks;
841
842                /**
843         *
844         */
845                private RuleApplicationResult result;
846
847                /**
848         *
849         */
850                private StopWatch stopWatch;
851
852                /**
853         *
854         */
855                private RuleApplicationData(List<IUserSession> sessions) {
856                        this.sessions = sessions;
857                        numberseqs = new ArrayList<NumberSequence>();
858                        number2task = new HashMap<Integer,ITask>();
859                        stopWatch= new StopWatch();
860                        result =  new RuleApplicationResult();
861                }
862
863                /**
864                 * @return the tree
865                 */
866                private List<IUserSession> getSessions() {
867                        return sessions;
868                }
869
870               
871                private ArrayList<NumberSequence> getNumberSequences() {
872                        return numberseqs;
873                }
874
875       
876
877                /**
878         *
879         */
880                private void detectedAndReplacedTasks(boolean detectedAndReplacedTasks) {
881                        this.detectedAndReplacedTasks = detectedAndReplacedTasks;
882                }
883
884                /**
885         *
886         */
887                private boolean detectedAndReplacedTasks() {
888                        return detectedAndReplacedTasks;
889                }
890
891                /**
892                 * @return the result
893                 */
894                private RuleApplicationResult getResult() {
895                        return result;
896                }
897
898                /**
899                 * @return the stopWatch
900                 */
901                private StopWatch getStopWatch() {
902                        return stopWatch;
903                }
904
905                private HashMap<Integer,ITask> getNumber2Task() {
906                        return number2task;
907                }
908
909        }
910
911       
912
913
914}
Note: See TracBrowser for help on using the repository browser.