source: trunk/autoquest-core-tasktrees/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/SequenceForTaskDetectionRule.java @ 1337

Last change on this file since 1337 was 1337, checked in by pharms, 10 years ago
  • solved problem with iteration detection, that caused detected iterations at the end of a session to be filled with iterated events of the next session(s).
File size: 38.1 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.HashMap;
18import java.util.HashSet;
19import java.util.Iterator;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.Map;
23import java.util.Set;
24import java.util.logging.Level;
25
26import de.ugoe.cs.autoquest.tasktrees.taskequality.TaskEquality;
27import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
28import de.ugoe.cs.autoquest.tasktrees.treeifc.IIterationInstance;
29import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
30import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelectionInstance;
31import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
32import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequenceInstance;
33import de.ugoe.cs.autoquest.tasktrees.treeifc.ITask;
34import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskBuilder;
35import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskFactory;
36import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
37import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstanceList;
38import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
39import de.ugoe.cs.autoquest.usageprofiles.SymbolMap;
40import de.ugoe.cs.autoquest.usageprofiles.TrieProcessor;
41import de.ugoe.cs.util.StopWatch;
42import de.ugoe.cs.util.console.Console;
43
44/**
45 * <p>
46 * TODO comment
47 * </p>
48 *
49 * @author Patrick Harms
50 */
51class SequenceForTaskDetectionRule implements ISessionScopeRule {
52   
53    /**
54     * <p>
55     * the task factory to be used for creating substructures for the temporal
56     * relationships identified during rule
57     * </p>
58     */
59    private ITaskFactory taskFactory;
60    /**
61     * <p>
62     * the task builder to be used for creating substructures for the temporal relationships
63     * identified during rule application
64     * </p>
65     */
66    private ITaskBuilder taskBuilder;
67
68    /**
69     * <p>
70     * the task comparator to be used for comparing tasks for preparation
71     * </p>
72     */
73    private TaskHandlingStrategy preparationTaskHandlingStrategy;
74   
75    /**
76     * <p>
77     * the task comparator to be used for comparing tasks during iteration detection an trie
78     * generation
79     * </p>
80     */
81    private TaskHandlingStrategy identityTaskHandlingStrategy;;
82   
83    /**
84     * <p>
85     * instantiates the rule and initializes it with a task equality rule manager and the minimal
86     * task equality identified sublist must have to consider them as iterated.
87     * </p>
88     */
89    SequenceForTaskDetectionRule(TaskEquality minimalTaskEquality,
90                                 ITaskFactory taskFactory,
91                                 ITaskBuilder taskBuilder)
92    {
93        this.taskFactory = taskFactory;
94        this.taskBuilder = taskBuilder;
95       
96        this.preparationTaskHandlingStrategy = new TaskHandlingStrategy(minimalTaskEquality);
97        this.identityTaskHandlingStrategy = new TaskHandlingStrategy(TaskEquality.IDENTICAL);
98    }
99
100    /* (non-Javadoc)
101     * @see java.lang.Object#toString()
102     */
103    @Override
104    public String toString() {
105        return "SequenceForTaskDetectionRule";
106    }
107
108    /* (non-Javadoc)
109     * @see de.ugoe.cs.autoquest.tasktrees.temporalrelation.ISessionScopeRule#apply(java.util.List)
110     */
111    @Override
112    public RuleApplicationResult apply(List<IUserSession> sessions) {
113        RuleApplicationData appData = new RuleApplicationData(sessions);
114       
115        // this is the real rule application. Loop while something is replaced.
116        harmonizeEventTaskInstancesModel(appData);
117        do {
118            System.out.println();
119           
120            appData.getStopWatch().start("whole loop");
121            detectAndReplaceIterations(appData);
122
123            appData.getStopWatch().start("task replacement");
124            detectAndReplaceTasks(appData);
125            appData.getStopWatch().stop("task replacement");
126            appData.getStopWatch().stop("whole loop");
127           
128            //((TaskTreeNodeComparator) taskComparator).getStopWatch().dumpStatistics(System.out);
129            //((TaskTreeNodeComparator) taskComparator).getStopWatch().reset();
130           
131            appData.getStopWatch().dumpStatistics(System.out);
132            appData.getStopWatch().reset();
133           
134        }
135        while (appData.detectedAndReplacedTasks());
136       
137        System.out.println
138            ("created " + appData.getResult().getNewlyCreatedTasks().size() +
139             " new tasks and " + appData.getResult().getNewlyCreatedTaskInstances().size() +
140             " appropriate instances\n");
141       
142        if ((appData.getResult().getNewlyCreatedTasks().size() > 0) ||
143            (appData.getResult().getNewlyCreatedTaskInstances().size() > 0))
144        {
145            appData.getResult().setRuleApplicationStatus(RuleApplicationStatus.FINISHED);
146        }
147       
148        return appData.getResult();
149    }
150
151    /**
152     * <p>
153     * TODO: comment
154     * </p>
155     *
156     * @param appData
157     */
158    private void harmonizeEventTaskInstancesModel(RuleApplicationData appData) {
159        Console.traceln(Level.INFO, "harmonizing task model of event task instances");
160        appData.getStopWatch().start("harmonizing event tasks");
161
162        SymbolMap<ITaskInstance, ITask> uniqueTasks =
163            preparationTaskHandlingStrategy.createSymbolMap();
164        TaskInstanceComparator comparator = preparationTaskHandlingStrategy.getTaskComparator();
165       
166        int unifiedTasks = 0;
167        ITask task;
168        List<IUserSession> sessions = appData.getSessions();
169        for (IUserSession session : sessions) {
170            Console.traceln(Level.FINE, "handling session " + session);
171            for (ITaskInstance taskInstance : session) {
172                task = uniqueTasks.getValue(taskInstance);
173               
174                if (task == null) {
175                    uniqueTasks.addSymbol(taskInstance, taskInstance.getTask());
176                }
177                else {
178                    taskBuilder.setTask(taskInstance, task);
179                    unifiedTasks++;
180                }
181            }
182           
183            comparator.clearBuffers();
184        }
185       
186        appData.getStopWatch().stop("harmonizing event tasks");
187        Console.traceln(Level.INFO, "harmonized " + unifiedTasks + " task occurrences (still " +
188                        uniqueTasks.size() + " different tasks)");
189
190        appData.getStopWatch().dumpStatistics(System.out);
191        appData.getStopWatch().reset();
192    }
193
194    /**
195     * @param appData
196     */
197    private void detectAndReplaceIterations(RuleApplicationData appData) {
198        Console.traceln(Level.FINE, "detecting iterations");
199        appData.getStopWatch().start("detecting iterations");
200       
201        List<IUserSession> sessions = appData.getSessions();
202       
203        Set<ITask> iteratedTasks = searchIteratedTasks(sessions);
204       
205        if (iteratedTasks.size() > 0) {
206            replaceIterationsOf(iteratedTasks, sessions, appData);
207        }
208       
209        appData.getStopWatch().stop("detecting iterations");
210        Console.traceln(Level.INFO, "replaced " + iteratedTasks.size() + " iterated tasks");
211    }
212
213    /**
214     *
215     */
216    private Set<ITask> searchIteratedTasks(List<IUserSession> sessions) {
217        Set<ITask> iteratedTasks = new HashSet<ITask>();
218        for (IUserSession session : sessions) {
219            for (int i = 0; i < (session.size() - 1); i++) {
220                // we prepared the task instances to refer to unique tasks, if they are treated
221                // as equal. Therefore, we just compare the identity of the tasks of the task
222                // instances
223                if (session.get(i).getTask() == session.get(i + 1).getTask()) {
224                    iteratedTasks.add(session.get(i).getTask());
225                }
226            }
227        }
228       
229        return iteratedTasks;
230    }
231
232    /**
233     *
234     */
235    private void replaceIterationsOf(Set<ITask>          iteratedTasks,
236                                     List<IUserSession>  sessions,
237                                     RuleApplicationData appData)
238    {
239        Map<ITask, IIteration> iterations = new HashMap<ITask, IIteration>();
240        Map<IIteration, List<IIterationInstance>> iterationInstances =
241                new HashMap<IIteration, List<IIterationInstance>>();
242           
243        for (ITask iteratedTask : iteratedTasks) {
244            IIteration iteration = taskFactory.createNewIteration();
245            iterations.put(iteratedTask, iteration);
246            iterationInstances.put(iteration, new LinkedList<IIterationInstance>());
247        }
248       
249        IIterationInstance iterationInstance;
250       
251        for (IUserSession session : sessions) {
252            int index = 0;
253            iterationInstance = null;
254
255            while (index < session.size()) {
256                // we prepared the task instances to refer to unique tasks, if they are treated
257                // as equal. Therefore, we just compare the identity of the tasks of the task
258                // instances
259                ITask currentTask = session.get(index).getTask();
260                IIteration iteration = iterations.get(currentTask);
261                if (iteration != null) {
262                    if ((iterationInstance == null) || (iterationInstance.getTask() != iteration))
263                    {
264                        iterationInstance = taskFactory.createNewTaskInstance(iteration);
265                        iterationInstances.get(iteration).add(iterationInstance);
266                        taskBuilder.addTaskInstance(session, index, iterationInstance);
267                        index++;
268                    }
269                   
270                    taskBuilder.addChild(iterationInstance, session.get(index));
271                    taskBuilder.removeTaskInstance(session, index);
272                }
273                else {
274                    if (iterationInstance != null) {
275                        iterationInstance = null;
276                    }
277                    index++;
278                }
279            }
280        }
281       
282        for (Map.Entry<IIteration, List<IIterationInstance>> entry : iterationInstances.entrySet())
283        {
284            harmonizeIterationInstancesModel(entry.getKey(), entry.getValue());
285        }
286    }
287
288    /**
289     *
290     */
291    private void harmonizeIterationInstancesModel(IIteration               iteration,
292                                                  List<IIterationInstance> iterationInstances)
293    {
294        List<ITask> iteratedTaskVariants = new LinkedList<ITask>();
295        TaskInstanceComparator comparator = preparationTaskHandlingStrategy.getTaskComparator();
296       
297        // merge the lexically different variants of iterated task to a unique list
298        for (IIterationInstance iterationInstance : iterationInstances) {
299            for (ITaskInstance executionVariant : iterationInstance) {
300                ITask candidate = executionVariant.getTask();
301           
302                boolean found = false;
303                for (ITask taskVariant : iteratedTaskVariants) {
304                    if (comparator.areLexicallyEqual(taskVariant, candidate)) {
305                        taskBuilder.setTask(executionVariant, taskVariant);
306                        found = true;
307                        break;
308                    }
309                }
310               
311                if (!found) {
312                    iteratedTaskVariants.add(candidate);
313                }
314            }
315        }
316       
317        // if there are more than one lexically different variant of iterated tasks, adapt the
318        // iteration model to be a selection of different variants. In this case also adapt
319        // the generated iteration instances to correctly contain selection instances. If there
320        // is only one variant of an iterated task, simply set this as the marked task of the
321        // iteration. In this case, the instances can be preserved as is
322        if (iteratedTaskVariants.size() > 1) {
323            ISelection selection = taskFactory.createNewSelection();
324           
325            for (ITask variant : iteratedTaskVariants) {
326                taskBuilder.addChild(selection, variant);
327            }
328           
329            taskBuilder.setMarkedTask(iteration, selection);
330           
331            for (IIterationInstance instance : iterationInstances) {
332                for (int i = 0; i < instance.size(); i++) {
333                    ISelectionInstance selectionInstance =
334                        taskFactory.createNewTaskInstance(selection);
335                    taskBuilder.setChild(selectionInstance, instance.get(i));
336                    taskBuilder.setTaskInstance(instance, i, selectionInstance);
337                }
338            }
339        }
340        else {
341            taskBuilder.setMarkedTask(iteration, iteratedTaskVariants.get(0));
342        }
343    }
344
345    /**
346     * @param appData
347     */
348    private void detectAndReplaceTasks(RuleApplicationData appData) {
349        Console.traceln(Level.FINE, "detecting and replacing tasks");
350        appData.getStopWatch().start("detecting tasks");
351       
352        getSequencesOccuringMostOften(appData);
353
354        appData.getStopWatch().stop("detecting tasks");
355        appData.getStopWatch().start("replacing tasks");
356       
357        replaceSequencesOccurringMostOften(appData);
358       
359        appData.getStopWatch().stop("replacing tasks");
360        Console.traceln(Level.INFO, "detected and replaced " + appData.getLastFoundTasks().size() +
361                        " tasks occuring " + appData.getLastFoundTasks().getOccurrenceCount() +
362                        " times");
363    }
364
365    /**
366     * @return
367     */
368    private void getSequencesOccuringMostOften(RuleApplicationData appData) {
369        Console.traceln(Level.FINE, "determining most prominent tasks");
370
371        Tasks tasks;
372        boolean createNewTrie = (appData.getLastTrie() == null) ||
373            appData.detectedAndReplacedTasks(); // tree has changed
374       
375        do {
376            if (createNewTrie) {
377                createNewTrie(appData);
378            }
379           
380            MaxCountAndLongestTasksFinder finder = new MaxCountAndLongestTasksFinder();
381            appData.getLastTrie().process(finder);
382       
383            tasks = finder.getFoundTasks();
384           
385            createNewTrie = false;
386           
387            for (List<ITaskInstance> task : tasks) {
388                if (task.size() >= appData.getTrainedSequenceLength()) {
389                    // Trie must be recreated with a longer sequence length to be sure that
390                    // the found sequences occurring most often are found in their whole length
391                    appData.setTrainedSequenceLength(appData.getTrainedSequenceLength() + 1);
392                    createNewTrie = true;
393                    break;
394                }
395            }
396        }
397        while (createNewTrie);
398       
399        // create a normal trie for comparison
400        /*System.out.println("creating test trie for comparison");
401       
402        appData.getStopWatch().start("testtrie");
403        Trie<ITaskInstance> trie = new Trie<ITaskInstance>(identityTaskComparator);
404       
405        for (IUserSession session : appData.getSessions()) {
406            trie.train(session.getExecutedTasks(), appData.getTrainedSequenceLength());
407        }
408       
409        appData.getStopWatch().stop("testtrie");
410       
411        MaxCountAndLongestTasksFinder finder = new MaxCountAndLongestTasksFinder();
412        trie.process(finder);
413
414        boolean allTasksEqual = finder.getFoundTasks().size() == tasks.size();
415       
416        allTasksEqual &= finder.getFoundTasks().occurrenceCount == tasks.occurrenceCount;
417       
418        for (List<ITaskInstance> task1 : finder.getFoundTasks()) {
419            boolean foundTask = false;
420            for (List<ITaskInstance> task2 : tasks) {
421                boolean tasksEqual = false;
422                if (task1.size() == task2.size()) {
423                    tasksEqual = true;
424                    for (int i = 0; i < task1.size(); i++) {
425                        if (!identityTaskComparator.equals(task1.get(i).getTask(), task2.get(i).getTask()))
426                        {
427                            tasksEqual = false;
428                        }
429                    }
430                }
431               
432                if (tasksEqual) {
433                    foundTask = true;
434                    break;
435                }
436            }
437           
438            if (!foundTask) {
439                System.out.println("different is " + task1);
440                allTasksEqual = false;
441                break;
442            }
443        }
444       
445        if (!allTasksEqual) {
446            System.out.println(finder.getFoundTasks());
447            System.out.println(tasks);
448           
449            throw new IllegalArgumentException("both tries calculated different subsequences");
450        }
451       
452        /*TrieStatisticsDumper dumper = new TrieStatisticsDumper();
453        appData.getLastTrie().process(dumper);
454        dumper.dumpCounters();*/
455       
456        appData.setLastFoundTasks(tasks);
457
458        Console.traceln(Level.FINE, "found " + appData.getLastFoundTasks().size() + " tasks " +
459                        "occurring " + appData.getLastFoundTasks().getOccurrenceCount() + " times");
460    }
461
462    /**
463     * @param parent
464     * @return
465     */
466    private void createNewTrie(RuleApplicationData appData) {
467        Console.traceln(Level.FINER, "training trie with a maximum sequence length of " +
468                        appData.getTrainedSequenceLength());
469
470        appData.getStopWatch().start("training trie");
471       
472        // we prepared the task instances to refer to unique tasks, if they are treated
473        // as equal. Therefore, we just compare the identity of the tasks of the task
474        // instances
475        appData.setLastTrie(new TaskInstanceTrie(identityTaskHandlingStrategy));
476   
477        appData.getLastTrie().trainSessions
478            (appData.getSessions(), appData.getTrainedSequenceLength());
479       
480        appData.getStopWatch().stop("training trie");
481    }
482
483    /**
484     * @param appData
485     */
486    private void replaceSequencesOccurringMostOften(RuleApplicationData appData) {
487        appData.detectedAndReplacedTasks(false);
488
489        if ((appData.getLastFoundTasks().size() > 0) &&
490            (appData.getLastFoundTasks().getOccurrenceCount() > 1))
491        {
492            Console.traceln(Level.FINER, "replacing tasks occurrences");
493
494            for (List<ITaskInstance> task : appData.getLastFoundTasks()) {
495                ISequence sequence = taskFactory.createNewSequence();
496               
497                Console.traceln(Level.FINEST, "replacing " + sequence.getId() + ": " + task);
498
499                List<ISequenceInstance> sequenceInstances =
500                    replaceTaskOccurrences(task, appData.getSessions(), sequence);
501               
502                harmonizeSequenceInstancesModel(sequence, sequenceInstances,task.size());
503                appData.detectedAndReplacedTasks
504                    (appData.detectedAndReplacedTasks() || (sequenceInstances.size() > 0));
505
506                if (sequenceInstances.size() < appData.getLastFoundTasks().getOccurrenceCount()) {
507                    Console.traceln(Level.FINE, sequence.getId() + ": replaced task only " +
508                                    sequenceInstances.size() + " times instead of expected " +
509                                    appData.getLastFoundTasks().getOccurrenceCount());
510                }
511            }
512        }
513       
514    }
515
516    /**
517     *
518     */
519    private void harmonizeSequenceInstancesModel(ISequence               sequence,
520                                                 List<ISequenceInstance> sequenceInstances,
521                                                 int                     sequenceLength)
522    {
523        TaskInstanceComparator comparator = preparationTaskHandlingStrategy.getTaskComparator();
524       
525        // ensure for each subtask that lexically different variants are preserved
526        for (int subTaskIndex = 0; subTaskIndex < sequenceLength; subTaskIndex++) {
527            List<ITask> subTaskVariants = new LinkedList<ITask>();
528           
529            for (ISequenceInstance sequenceInstance : sequenceInstances) {
530                ITask candidate = sequenceInstance.get(subTaskIndex).getTask();
531               
532                boolean found = false;
533               
534                for (int i = 0; i < subTaskVariants.size(); i++) {
535                    if (comparator.areLexicallyEqual(subTaskVariants.get(i), candidate)) {
536                        taskBuilder.setTask
537                            (sequenceInstance.get(subTaskIndex), subTaskVariants.get(i));
538                       
539                        found = true;
540                        break;
541                    }
542                }
543               
544                if (!found) {
545                    subTaskVariants.add(candidate);
546                }
547            }
548           
549            // if there are more than one lexically different variant of the sub task at
550            // the considered position, adapt the sequence model at that position to have
551            // a selection of the different variants. In this case also adapt the
552            // generated sequence instances to correctly contain selection instances. If
553            // there is only one variant of sub tasks at the given position, simply set
554            // this variant as the sub task of the selection. In this case, the instances
555            // can be preserved as is
556            if (subTaskVariants.size() > 1) {
557                ISelection selection = taskFactory.createNewSelection();
558               
559                for (ITask variant : subTaskVariants) {
560                    taskBuilder.addChild(selection, variant);
561                }
562               
563                taskBuilder.addChild(sequence, selection);
564               
565                for (ISequenceInstance instance : sequenceInstances) {
566                    ISelectionInstance selectionInstance =
567                        taskFactory.createNewTaskInstance(selection);
568                    taskBuilder.setChild(selectionInstance, instance.get(subTaskIndex));
569                    taskBuilder.setTaskInstance(instance, subTaskIndex, selectionInstance);
570                }
571            }
572            else if (subTaskVariants.size() == 1) {
573                taskBuilder.addChild(sequence, subTaskVariants.get(0));
574            }
575        }
576    }
577
578    /**
579     * @param tree
580     */
581    private List<ISequenceInstance> replaceTaskOccurrences(List<ITaskInstance> task,
582                                                           List<IUserSession>  sessions,
583                                                           ISequence           temporalTaskModel)
584    {
585        List<ISequenceInstance> sequenceInstances = new LinkedList<ISequenceInstance>();
586       
587        for (IUserSession session : sessions) {
588            int index = -1;
589               
590            do {
591                index = getSubListIndex(session, task, ++index);
592
593                if (index > -1) {
594                    sequenceInstances.add
595                        (RuleUtils.createNewSubSequenceInRange
596                             (session, index, index + task.size() - 1, temporalTaskModel,
597                              taskFactory, taskBuilder));
598                }
599            }
600            while (index > -1);
601        }
602       
603        return sequenceInstances;
604    }
605
606    /**
607     * @param trie
608     * @param object
609     * @return
610     */
611    private int getSubListIndex(ITaskInstanceList   list,
612                                List<ITaskInstance> subList,
613                                int                 startIndex)
614    {
615        boolean matchFound;
616        int result = -1;
617       
618        for (int i = startIndex; i <= list.size() - subList.size(); i++) {
619            matchFound = true;
620           
621            for (int j = 0; j < subList.size(); j++) {
622                // we prepared the task instances to refer to unique tasks, if they are treated
623                // as equal. Therefore, we just compare the identity of the tasks of the task
624                // instances
625                if (list.get(i + j).getTask() != subList.get(j).getTask()) {
626                    matchFound = false;
627                    break;
628                }
629            }
630           
631            if (matchFound) {
632                result = i;
633                break;
634            }
635        }
636       
637        return result;
638    }
639
640    /**
641     * @param trie
642     * @param object
643     * @return
644     */
645    private int getSubListIndex(List<ITaskInstance> list,
646                                List<ITaskInstance> subList,
647                                int                 startIndex)
648    {
649        boolean matchFound;
650        int result = -1;
651       
652        for (int i = startIndex; i <= list.size() - subList.size(); i++) {
653            matchFound = true;
654           
655            for (int j = 0; j < subList.size(); j++) {
656                // we prepared the task instances to refer to unique tasks, if they are treated
657                // as equal. Therefore, we just compare the identity of the tasks of the task
658                // instances
659                if (list.get(i + j) != subList.get(j)) {
660                    matchFound = false;
661                    break;
662                }
663            }
664           
665            if (matchFound) {
666                result = i;
667                break;
668            }
669        }
670       
671        return result;
672    }
673   
674    /**
675     * @author Patrick Harms
676     */
677    private class MaxCountAndLongestTasksFinder implements TrieProcessor<ITaskInstance> {
678       
679        /**
680         *
681         */
682        private int currentCount;
683       
684        /**
685         *
686         */
687        private List<List<ITaskInstance>> foundTasks = new LinkedList<List<ITaskInstance>>();
688
689        /**
690         *
691         */
692        public MaxCountAndLongestTasksFinder() {
693            super();
694            this.currentCount = 0;
695        }
696
697        /* (non-Javadoc)
698         * @see de.ugoe.cs.autoquest.usageprofiles.TrieProcessor#process(java.util.List, int)
699         */
700        @Override
701        public TrieProcessor.Result process(List<ITaskInstance> foundTask, int count) {
702            if (foundTask.size() < 2) {
703                // ignore single tasks
704                return TrieProcessor.Result.CONTINUE;
705            }
706           
707            if (count < 2) {
708                // ignore singular occurrences
709                return TrieProcessor.Result.SKIP_NODE;
710            }
711
712            if (this.currentCount > count) {
713                // ignore this children of this task, as they may have only smaller counts than
714                // the already found tasks
715                return TrieProcessor.Result.SKIP_NODE;
716            }
717           
718            if (this.currentCount < count) {
719                // the provided task occurs more often that all tasks found so far.
720                // clear all found tasks and use the new count as the one searched for
721                foundTasks.clear();
722                this.currentCount = count;
723            }
724           
725            if (this.currentCount == count) {
726                // the task is of interest. Sort it into the other found tasks so that
727                // the longest come first
728                boolean added = false;
729                for (int i = 0; i < foundTasks.size(); i++) {
730                    if (foundTasks.get(i).size() < foundTask.size()) {
731                        // defensive copy
732                        foundTasks.add(i, new LinkedList<ITaskInstance>(foundTask)); // defensive copy
733                        added = true;
734                        break;
735                    }
736                }
737               
738                if (!added) {
739                    foundTasks.add(new LinkedList<ITaskInstance>(foundTask)); // defensive copy
740                }
741            }
742           
743            return TrieProcessor.Result.CONTINUE;
744        }
745
746        /**
747         *  @return
748         */
749        public Tasks getFoundTasks() {
750            removePermutationsOfShorterTasks();
751            return new Tasks(currentCount, foundTasks);
752        }
753
754        /**
755         *
756         */
757        private void removePermutationsOfShorterTasks() {
758            // now iterate the sorted list and for each task remove all other tasks that are shorter
759            // (i.e. come later in the sorted list) and that represent a subpart of the task
760            for (int i = 0; i < foundTasks.size(); i++) {
761                for (int j = i + 1; j < foundTasks.size();) {
762                    if (foundTasks.get(j).size() < foundTasks.get(i).size()) {
763                        // found a task that is a potential subtask. Check for this and remove the
764                        // subtask if needed
765                        List<ITaskInstance> longTask = foundTasks.get(i);
766                        List<ITaskInstance> shortTask = foundTasks.get(j);
767                       
768                        if (getSubListIndex(longTask, shortTask, 0) > -1) {
769                            foundTasks.remove(j);
770                        }
771                        else {
772                            j++;
773                        }
774                    }
775                    else {
776                        j++;
777                    }
778                }
779            }
780        }
781
782    }
783   
784//    /**
785//     * @author Patrick Harms
786//     */
787//    private class TrieStatisticsDumper implements TrieProcessor<ITaskInstance> {
788//       
789//        /**
790//         *
791//         */
792//        private Map<Integer, Integer> counters = new HashMap<Integer, Integer>();
793//       
794//        /* (non-Javadoc)
795//         * @see de.ugoe.cs.autoquest.usageprofiles.TrieProcessor#process(java.util.List, int)
796//         */
797//        @Override
798//        public TrieProcessor.Result process(List<ITaskInstance> subsequence, int count) {
799//            if (subsequence.size() == 1) {
800//                Integer value = counters.get(count);
801//               
802//                if (value == null) {
803//                    value = 0;
804//                }
805//               
806//                counters.put(count, value + 1);
807//               
808//                return TrieProcessor.Result.CONTINUE;
809//            }
810//            else {
811//                // ignore singular occurrences
812//                return TrieProcessor.Result.SKIP_NODE;
813//            }
814//        }
815//
816//        /**
817//         *  @return
818//         */
819//        public void dumpCounters() {
820//            int dumpedCounters = 0;
821//           
822//            int count = 1;
823//            while (dumpedCounters < counters.size()) {
824//                Integer value = counters.get(count++);
825//                if (value != null) {
826//                    System.out.println(value + " symbols occurred " + count + " times");
827//                    dumpedCounters++;
828//                }
829//            }
830//        }
831//
832//    }
833   
834    /**
835     *
836     */
837    private static class RuleApplicationData {
838       
839        /**
840         *
841         */
842        private List<IUserSession> sessions;
843       
844        /**
845         *
846         */
847        private TaskInstanceTrie lastTrie;
848       
849        /**
850         * default and minimum trained sequence length is 3
851         */
852        private int trainedSequenceLength = 3;
853       
854        /**
855         *
856         */
857        private Tasks lastFoundTasks = new Tasks(Integer.MAX_VALUE, null);
858       
859        /**
860         *
861         */
862        private boolean detectedAndReplacedTasks;
863       
864        /**
865         *
866         */
867        private RuleApplicationResult result = new RuleApplicationResult();
868       
869        /**
870         *
871         */
872        private StopWatch stopWatch = new StopWatch();
873       
874        /**
875         *
876         */
877        private RuleApplicationData(List<IUserSession> sessions) {
878            this.sessions = sessions;
879        }
880
881        /**
882         * @return the tree
883         */
884        private List<IUserSession> getSessions() {
885            return sessions;
886        }
887
888        /**
889         * @param lastTrie the lastTrie to set
890         */
891        private void setLastTrie(TaskInstanceTrie lastTrie) {
892            this.lastTrie = lastTrie;
893        }
894
895        /**
896         * @return the lastTrie
897         */
898        private TaskInstanceTrie getLastTrie() {
899            return lastTrie;
900        }
901
902        /**
903         * @param trainedSequenceLength the trainedSequenceLength to set
904         */
905        private void setTrainedSequenceLength(int trainedSequenceLength) {
906            this.trainedSequenceLength = trainedSequenceLength;
907        }
908
909        /**
910         * @return the trainedSequenceLength
911         */
912        private int getTrainedSequenceLength() {
913            return trainedSequenceLength;
914        }
915
916        /**
917         * @param lastFoundSequences the lastFoundSequences to set
918         */
919        private void setLastFoundTasks(Tasks lastFoundSequences) {
920            this.lastFoundTasks = lastFoundSequences;
921        }
922
923        /**
924         * @return the lastFoundSequences
925         */
926        private Tasks getLastFoundTasks() {
927            return lastFoundTasks;
928        }
929
930        /**
931         *
932         */
933        private void detectedAndReplacedTasks(boolean detectedAndReplacedTasks) {
934            this.detectedAndReplacedTasks = detectedAndReplacedTasks;
935        }
936
937        /**
938         *
939         */
940        private boolean detectedAndReplacedTasks() {
941            return detectedAndReplacedTasks;
942        }
943       
944        /**
945         * @return the result
946         */
947        private RuleApplicationResult getResult() {
948            return result;
949        }
950
951        /**
952         * @return the stopWatch
953         */
954        private StopWatch getStopWatch() {
955            return stopWatch;
956        }
957
958    }
959   
960
961    /**
962     * @author Patrick Harms
963     */
964    private static class Tasks implements Iterable<List<ITaskInstance>> {
965       
966        /**
967         *
968         */
969        private int occurrenceCount;
970       
971        /**
972         *
973         */
974        private List<List<ITaskInstance>> sequences;
975
976        /**
977         * @param occurrenceCount
978         * @param sequences
979         */
980        private Tasks(int occurrenceCount, List<List<ITaskInstance>> sequences) {
981            super();
982            this.occurrenceCount = occurrenceCount;
983            this.sequences = sequences;
984        }
985
986        /**
987         * @return
988         */
989        private int getOccurrenceCount() {
990            return occurrenceCount;
991        }
992
993        /**
994         * @return
995         */
996        private int size() {
997            return this.sequences.size();
998        }
999
1000        /**
1001         *
1002         */
1003
1004        /* (non-Javadoc)
1005         * @see java.lang.Iterable#iterator()
1006         */
1007        @Override
1008        public Iterator<List<ITaskInstance>> iterator() {
1009            return this.sequences.iterator();
1010        }
1011
1012        /* (non-Javadoc)
1013         * @see java.lang.Object#toString()
1014         */
1015        @Override
1016        public String toString() {
1017            StringBuffer result = new StringBuffer();
1018            result.append(this.occurrenceCount);
1019            result.append(" occurrences:\n");
1020           
1021            for (List<ITaskInstance> task : sequences) {
1022                result.append(task);
1023                result.append("\n");
1024            }
1025           
1026            return result.toString();
1027        }
1028
1029    }
1030   
1031    // methods for internal testing
1032//    private void checkMatchingOfSessions(List<List<Event>>  flattenedSessions,
1033//                                         List<IUserSession> sessions,
1034//                                         String             when)
1035//    {
1036//        List<List<Event>> currentFlattenedSessions = flattenSessions(sessions);
1037//        if (flattenedSessions.size() != currentFlattenedSessions.size()) {
1038//            System.out.println("################## number of sessions changed after " + when);
1039//        }
1040//        else {
1041//            for (int i = 0; i < flattenedSessions.size(); i++) {
1042//                List<Event> expected = flattenedSessions.get(i);
1043//                List<Event> current = currentFlattenedSessions.get(i);
1044//           
1045//                if (expected.size() != current.size()) {
1046//                    System.out.println
1047//                        ("################## length of session " + i + " changed after " + when);
1048//                }
1049//                else {
1050//                    for (int j = 0; j < expected.size(); j++) {
1051//                        if (!expected.get(j).equals(current.get(j))) {
1052//                            System.out.println("################## event " + j + " of session " +
1053//                                               i + " changed after " + when);
1054//                        }
1055//                    }
1056//                }
1057//            }     
1058//        }
1059//    }
1060//
1061//    private List<List<Event>> flattenSessions(List<IUserSession> sessions) {
1062//        List<List<Event>> flattenedSessions = new ArrayList<List<Event>>();
1063//        for (IUserSession session : sessions) {
1064//            List<Event> flattenedUserSession = new ArrayList<Event>();
1065//            flatten(session, flattenedUserSession);
1066//            flattenedSessions.add(flattenedUserSession);
1067//        }
1068//
1069//        return flattenedSessions;
1070//    }
1071//
1072//    private void flatten(IUserSession iUserSession, List<Event> flattenedUserSession) {
1073//        for (ITaskInstance instance : iUserSession) {
1074//            flatten(instance, flattenedUserSession);
1075//        }
1076//    }
1077//
1078//    private void flatten(ITaskInstance instance, List<Event> flattenedUserSession) {
1079//        if (instance instanceof ITaskInstanceList) {
1080//            for (ITaskInstance child : (ITaskInstanceList) instance) {
1081//                flatten(child, flattenedUserSession);
1082//            }
1083//        }
1084//        else if (instance instanceof ISelectionInstance) {
1085//            flatten(((ISelectionInstance) instance).getChild(), flattenedUserSession);
1086//        }
1087//        else if (instance instanceof IOptionalInstance) {
1088//            flatten(((IOptionalInstance) instance).getChild(), flattenedUserSession);
1089//        }
1090//        else if (instance instanceof IEventTaskInstance) {
1091//            flattenedUserSession.add(((IEventTaskInstance) instance).getEvent());
1092//        }
1093//    }
1094
1095}
Note: See TracBrowser for help on using the repository browser.