Ignore:
Timestamp:
09/16/14 17:48:28 (10 years ago)
Author:
rkrimmel
Message:

I did it, i fixed ALL the bugs.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/autoquest-core-tasktrees-alignment/src/main/java/de/ugoe/cs/autoquest/tasktrees/temporalrelation/SequenceForTaskDetectionRuleAlignment.java

    r1743 r1747  
    5858/** 
    5959 * <p> 
    60  * This class implements the major rule for creating task trees based on a set 
    61  * of recorded user sessions. For this, it first harmonizes all tasks. This 
    62  * eases later comparison. Then it searches the sessions for iterations and 
    63  * replaces them accordingly. Then it searches for sub sequences using alignment 
    64  * algorithms For each found sub sequence, it replaces the occurrences by 
    65  * creating appropriate {@link ISequence}s. Afterwards, again searches for 
    66  * iterations and then again for sub sequences until no more replacements are 
    67  * done. 
     60 * This class implements the major rule for creating task trees based on a set of recorded user 
     61 * sessions. For this, it first harmonizes all tasks. This eases later comparison. Then it searches 
     62 * the sessions for iterations and replaces them accordingly. Then it searches for sub sequences 
     63 * using alignment algorithms For each found sub sequence, it replaces the occurrences by creating 
     64 * appropriate {@link ISequence}s. Afterwards, again searches for iterations and then again for sub 
     65 * sequences until no more replacements are done. 
    6866 * </p> 
    6967 * <p> 
     
    7472public class SequenceForTaskDetectionRuleAlignment implements ISessionScopeRule { 
    7573 
    76         /** The n threads. */ 
    77         //public static int nThreads = Runtime.getRuntime().availableProcessors() - 1; 
    78         public static int nThreads = 1; 
    79  
    80         /** The iteration. */ 
    81         private int iteration = 0; 
    82  
    83         /** 
    84          * <p> 
    85          * the task factory to be used for creating substructures for the temporal 
    86          * relationships identified during rul application 
    87          * </p> 
    88          * . 
    89          */ 
    90         private final ITaskFactory taskFactory; 
    91  
    92         /** 
    93          * <p> 
    94          * the task builder to be used for creating substructures for the temporal 
    95          * relationships identified during rule application 
    96          * </p> 
    97          * . 
    98          */ 
    99         private final ITaskBuilder taskBuilder; 
    100  
    101         /** 
    102          * <p> 
    103          * the task handling strategy to be used for comparing tasks for 
    104          * preparation, i.e., before the tasks are harmonized 
    105          * </p> 
    106          */ 
    107         private final TaskHandlingStrategy preparationTaskHandlingStrategy; 
    108  
    109         /** 
    110          * <p> 
    111          * instantiates the rule and initializes it with a task equality to be 
    112          * considered when comparing tasks as well as a task factory and builder to 
    113          * be used for creating task structures. 
    114          * </p> 
    115          *  
    116          * @param minimalTaskEquality 
    117          *            the task equality to be considered when comparing tasks 
    118          * @param taskFactory 
    119          *            the task factory to be used for creating substructures 
    120          * @param taskBuilder 
    121          *            the task builder to be used for creating substructures 
    122          */ 
    123  
    124         SequenceForTaskDetectionRuleAlignment(TaskEquality minimalTaskEquality, 
    125                         ITaskFactory taskFactory, ITaskBuilder taskBuilder) { 
    126                 this.taskFactory = taskFactory; 
    127                 this.taskBuilder = taskBuilder; 
    128  
    129                 this.preparationTaskHandlingStrategy = new TaskHandlingStrategy( 
    130                                 minimalTaskEquality); 
    131         } 
    132  
    133         /* 
    134          * (non-Javadoc) 
    135          *  
    136          * @see 
    137          * de.ugoe.cs.autoquest.tasktrees.temporalrelation.ISessionScopeRule#apply 
    138          * (java.util.List) 
    139          */ 
    140         @Override 
    141         public RuleApplicationResult apply(List<IUserSession> sessions) { 
    142                 final RuleApplicationData appData = new RuleApplicationData(sessions); 
    143  
    144                 harmonizeEventTaskInstancesModel(appData); 
    145  
    146                 Console.traceln(Level.INFO, "generating substitution matrix from " 
    147                                 + appData.getUniqueTasks().size() + " unique tasks"); 
    148                 appData.getStopWatch().start("substitution matrix"); 
    149                 appData.getSubmat().generate(appData.getUniqueTasks()); 
    150                 appData.getStopWatch().stop("substitution matrix"); 
    151  
    152                 Console.traceln(Level.INFO, "Starting main loop"); 
    153                 do { 
    154                         Console.traceln(Level.INFO, "Iteration Number: " + iteration); 
    155                         iteration++; 
    156                         appData.detectedAndReplacedTasks = false; 
    157                         appData.getStopWatch().start("whole loop"); 
    158                         detectAndReplaceIterations(appData); 
    159                         appData.getStopWatch().start("task replacement"); 
    160                         // Just does anything if the substitution matrix is created with the 
    161                         // option to do so 
    162                         appData.updateSubstitutionMatrix(); 
    163                         detectAndReplaceTasks(appData); // 
    164                         appData.getStopWatch().stop("task replacement"); 
    165                         appData.getStopWatch().stop("whole loop"); 
    166                         // appData.getStopWatch().dumpStatistics(System.out); 
    167                         appData.getStopWatch().reset(); 
    168  
    169                 } while (appData.detectedAndReplacedTasks()); 
    170  
    171                 Console.println("created " 
    172                                 + appData.getResult().getNewlyCreatedTasks().size() 
    173                                 + " new tasks and " 
    174                                 + appData.getResult().getNewlyCreatedTaskInstances().size() 
    175                                 + " appropriate instances\n"); 
    176  
    177                 if ((appData.getResult().getNewlyCreatedTasks().size() > 0) 
    178                                 || (appData.getResult().getNewlyCreatedTaskInstances().size() > 0)) { 
    179                         appData.getResult().setRuleApplicationStatus( 
    180                                         RuleApplicationStatus.FINISHED); 
    181                 } 
    182                 // new TaskTreeValidator().validate(appData.getSessions()); 
    183                 return appData.getResult(); 
    184         } 
    185  
    186         /** 
    187          * Creates the number sequences. 
    188          * 
    189          * @param appData 
    190          *            the app data 
    191          * @return the array list 
    192          */ 
    193         private ArrayList<NumberSequence> createNumberSequences( 
    194                         RuleApplicationData appData) { 
    195                 final ArrayList<NumberSequence> result = new ArrayList<NumberSequence>(); 
    196                 for (int i = 0; i < appData.getSessions().size(); i++) { 
    197                         final IUserSession session = appData.getSessions().get(i); 
    198                         final NumberSequence templist = new NumberSequence(session.size()); 
    199                         for (int j = 0; j < session.size(); j++) { 
    200                                 final ITaskInstance taskInstance = session.get(j); 
    201                                 templist.getSequence()[j] = taskInstance.getTask().getId(); 
    202                         } 
    203                         // Each NumberSequence is identified by its id, beginning to count 
    204                         // at zero 
    205                         templist.setId(i); 
    206                         result.add(templist); 
    207                 } 
    208                 return result; 
    209         } 
    210  
    211         /** 
    212          * <p> 
    213          * searches for direct iterations of single tasks in all sequences and 
    214          * replaces them with {@link IIteration}s, respectively appropriate 
    215          * instances. Also all single occurrences of a task that is iterated 
    216          * somewhen are replaced with iterations to have again an efficient way for 
    217          * task comparisons. 
    218          * </p> 
    219          *  
    220          * @param appData 
    221          *            the rule application data combining all data used for applying 
    222          *            this rule 
    223          */ 
    224         private void detectAndReplaceIterations(RuleApplicationData appData) { 
    225                 Console.traceln(Level.FINE, "detecting iterations"); 
    226                 appData.getStopWatch().start("detecting iterations"); 
    227  
    228                 final List<IUserSession> sessions = appData.getSessions(); 
    229  
    230                 final Set<ITask> iteratedTasks = searchIteratedTasks(sessions); 
    231  
    232                 if (iteratedTasks.size() > 0) { 
    233                         replaceIterationsOf(iteratedTasks, sessions, appData); 
    234                 } 
    235  
    236                 appData.getStopWatch().stop("detecting iterations"); 
    237                 Console.traceln(Level.INFO, "replaced " + iteratedTasks.size() 
    238                                 + " iterated tasks"); 
    239         } 
    240  
    241         /** 
    242          * Detect and replace tasks. 
    243          * 
    244          * @param appData 
    245          *            the rule application data combining all data used for applying 
    246          *            this rule 
    247          */ 
    248         private void detectAndReplaceTasks(RuleApplicationData appData) { 
    249                 Console.traceln(Level.FINE, "detecting and replacing tasks"); 
    250                 appData.getStopWatch().start("detecting tasks"); 
    251  
    252                 // Create NumberSequences 
    253                 appData.setNumberSequences(this.createNumberSequences(appData)); 
    254  
    255                 // Generate pairwise alignments 
    256                 // appData.setMatchseqs(generatePairwiseAlignments(appData)); 
    257                 generatePairwiseAlignments(appData); 
    258                 Console.traceln(Level.FINE, "Found " + appData.getMatchseqs().size() 
    259                                 + " results"); 
    260                  
    261                 //Nothing found, we can end here 
    262                 if(appData.getMatchseqs().size()==0) { 
    263                         appData.detectedAndReplacedTasks=false; 
    264                         return; 
    265                 } 
    266                  
    267                 // Searching each match in all other sessions, counting its occurences 
    268                 searchMatchesInAllSessions(appData); 
    269  
    270                 // Sort results to get the most occurring results 
    271                 Console.traceln(Level.INFO, "sorting " + appData.getMatchseqs().size() 
    272                                 + " results"); 
    273                 final Comparator<Match> comparator = new Comparator<Match>() { 
    274                         @Override 
    275                         public int compare(Match m1, Match m2) { 
    276                                 int cmp = m2.occurenceCount() - m1.occurenceCount(); 
    277                                 if (cmp != 0) { 
    278                                         return cmp; 
    279                                 } else { 
    280                                         cmp = m2.size() - m1.size(); 
    281                                         if (cmp != 0) { 
    282                                                 return cmp; 
    283                                         } else { 
    284                                                 // This should rarely happen 
    285                                                 cmp = m2.ocurrenceIDSum() - m1.ocurrenceIDSum(); 
    286                                                 if (cmp != 0) { 
    287                                                         return cmp; 
    288                                                 } else { 
    289                                                         cmp = m2.taskIdSum() - m1.taskIdSum(); 
    290  
    291                                                         return cmp; 
    292                                                 } 
    293                                         } 
    294                                 } 
    295                         } 
    296                 }; 
    297  
    298                 Collections.sort(appData.getMatchseqs(), comparator); 
    299                 appData.getStopWatch().stop("detecting tasks"); 
    300  
    301                 Console.traceln(Level.INFO, "Preparing replacments"); 
    302                 prepareReplacements(appData); 
    303                 Console.traceln(Level.INFO, "Replacing matches in sessions"); 
    304                 appData.getStopWatch().start("replacing tasks"); 
    305                 replaceMatches(appData); 
    306                 appData.getStopWatch().stop("replacing tasks"); 
    307         } 
    308  
    309         // TODO: DEBUG METHOD 
    310         @SuppressWarnings("unused") 
    311         private void printMatches(RuleApplicationData appData) { 
    312                 LinkedList<Match> matchseqs = appData.getMatchseqs(); 
    313                 if (iteration > 1) { 
    314                         System.out.println("PRINTING MATCHES"); 
    315                         for (Iterator<Match> it = matchseqs.iterator(); it.hasNext();) { 
    316                                 Match m = it.next(); 
    317                                 m.getFirstSequence().printSequence(); 
    318                                 m.getSecondSequence().printSequence(); 
    319                                 for (Iterator<MatchOccurrence> jt = m.getOccurences() 
    320                                                 .iterator(); jt.hasNext();) { 
    321                                         MatchOccurrence mo = jt.next(); 
    322                                         System.out.print(mo.getSequenceId() + " "); 
    323                                 } 
    324                                 System.out.println(); 
    325                                 System.out.println(); 
    326                         } 
    327                 } 
    328         } 
    329  
    330         /** 
    331          * <p> 
    332          * harmonizes the event task instances by unifying tasks. This is done, as 
    333          * initially the event tasks being equal with respect to the considered task 
    334          * equality are distinct objects. The comparison of these distinct objects 
    335          * is more time consuming than comparing the object references. 
    336          * </p> 
    337          *  
    338          * @param appData 
    339          *            the rule application data combining all data used for applying 
    340          *            this rule 
    341          * @return Returns the unique tasks symbol map 
    342          */ 
    343         private void harmonizeEventTaskInstancesModel(RuleApplicationData appData) { 
    344                 Console.traceln(Level.INFO, 
    345                                 "harmonizing task model of event task instances"); 
    346                 appData.getStopWatch().start("harmonizing event tasks"); 
    347                 final SymbolMap<ITaskInstance, ITask> uniqueTasks = preparationTaskHandlingStrategy 
    348                                 .createSymbolMap(); 
    349  
    350                 final TaskInstanceComparator comparator = preparationTaskHandlingStrategy 
    351                                 .getTaskComparator(); 
    352  
    353                 int unifiedTasks = 0; 
    354                 ITask task; 
    355                 final List<IUserSession> sessions = appData.getSessions(); 
    356                 for (int j = 0; j < sessions.size(); j++) { 
    357                         final IUserSession session = sessions.get(j); 
    358  
    359                         for (int i = 0; i < session.size(); i++) { 
    360                                 final ITaskInstance taskInstance = session.get(i); 
    361                                 task = uniqueTasks.getValue(taskInstance); 
    362  
    363                                 if (task == null) { 
    364                                         uniqueTasks.addSymbol(taskInstance, taskInstance.getTask()); 
    365                                         appData.getUniqueTasks().add(taskInstance.getTask()); 
    366                                         appData.getNumber2Task().put( 
    367                                                         taskInstance.getTask().getId(), 
    368                                                         taskInstance.getTask()); 
    369                                 } else { 
    370                                         taskBuilder.setTask(taskInstance, task); 
    371                                         unifiedTasks++; 
    372                                 } 
    373                         } 
    374                         comparator.clearBuffers(); 
    375                 } 
    376  
    377                 appData.getStopWatch().stop("harmonizing event tasks"); 
    378                 Console.traceln(Level.INFO, "harmonized " + unifiedTasks 
    379                                 + " task occurrences (still " + appData.getUniqueTasks().size() 
    380                                 + " different tasks)"); 
    381  
    382                 appData.getStopWatch().dumpStatistics(System.out); 
    383                 appData.getStopWatch().reset(); 
    384         } 
    385  
    386         /** 
    387          * <p> 
    388          * TODO clarify why this is done (in fact, ask Patrick Harms) 
    389          * </p> 
    390          * . 
    391          * 
    392          * @param iteration 
    393          *            the iteration 
    394          * @param iterationInstances 
    395          *            the iteration instances 
    396          */ 
    397         private void harmonizeIterationInstancesModel(IIteration iteration, 
    398                         List<IIterationInstance> iterationInstances) { 
    399                 final List<ITask> iteratedTaskVariants = new LinkedList<ITask>(); 
    400                 final TaskInstanceComparator comparator = preparationTaskHandlingStrategy 
    401                                 .getTaskComparator(); 
    402  
    403                 // merge the lexically different variants of iterated task to a unique 
    404                 // list 
    405                 for (final IIterationInstance iterationInstance : iterationInstances) { 
    406                         for (final ITaskInstance executionVariant : iterationInstance) { 
    407                                 final ITask candidate = executionVariant.getTask(); 
    408  
    409                                 boolean found = false; 
    410                                 for (final ITask taskVariant : iteratedTaskVariants) { 
    411                                         if (comparator.areLexicallyEqual(taskVariant, candidate)) { 
    412                                                 taskBuilder.setTask(executionVariant, taskVariant); 
    413                                                 found = true; 
    414                                                 break; 
    415                                         } 
    416                                 } 
    417  
    418                                 if (!found) { 
    419                                         iteratedTaskVariants.add(candidate); 
    420                                 } 
    421                         } 
    422                 } 
    423  
    424                 // if there are more than one lexically different variant of iterated 
    425                 // tasks, adapt the 
    426                 // iteration model to be a selection of different variants. In this case 
    427                 // also adapt 
    428                 // the generated iteration instances to correctly contain selection 
    429                 // instances. If there 
    430                 // is only one variant of an iterated task, simply set this as the 
    431                 // marked task of the 
    432                 // iteration. In this case, the instances can be preserved as is 
    433                 if (iteratedTaskVariants.size() > 1) { 
    434                         final ISelection selection = taskFactory.createNewSelection(); 
    435  
    436                         for (final ITask variant : iteratedTaskVariants) { 
    437                                 taskBuilder.addChild(selection, variant); 
    438                         } 
    439  
    440                         taskBuilder.setMarkedTask(iteration, selection); 
    441  
    442                         for (final IIterationInstance instance : iterationInstances) { 
    443                                 for (int i = 0; i < instance.size(); i++) { 
    444                                         final ISelectionInstance selectionInstance = taskFactory 
    445                                                         .createNewTaskInstance(selection); 
    446                                         taskBuilder.setChild(selectionInstance, instance.get(i)); 
    447                                         taskBuilder.setTaskInstance(instance, i, selectionInstance); 
    448                                 } 
    449                         } 
    450                 } else { 
    451                         taskBuilder.setMarkedTask(iteration, iteratedTaskVariants.get(0)); 
    452                 } 
    453         } 
    454  
    455         /** 
    456          * Match as sequence. 
    457          * 
    458          * @param appData 
    459          *            RuleApplicationData needed to keep track of all created tasks 
    460          * @param m 
    461          *            The match to be converted into a Task 
    462          * @return The task of the match with an ISequence as its root 
    463          */ 
    464         synchronized public ISequence matchAsSequence(RuleApplicationData appData, 
    465                         Match m) { 
    466                 final ISequence sequence = taskFactory.createNewSequence(); 
    467                 appData.newTaskCreated(sequence); 
    468  
    469                 final int[] first = m.getFirstSequence().getSequence(); 
    470                 final int[] second = m.getSecondSequence().getSequence(); 
    471  
    472                 // Both sequences of a match are equally long 
    473                 for (int i = 0; i < m.getFirstSequence().size(); i++) { 
    474  
    475                         // Two gaps aligned to each other: Have not seen it happening so 
    476                         // far, just to handle it 
    477                         if ((first[i] == -1) && (second[i] == -1)) { 
    478                                 // Do nothing here. 
    479                         } 
    480                         // Both events are equal, we can simply add the task referring to 
    481                         // the number 
    482                         else if (first[i] == second[i]) { 
    483                                 taskBuilder.addChild(sequence, 
    484                                                 appData.getNumber2Task().get(first[i])); 
    485                         } 
    486                         // We have a gap in the first sequence, we need to add the task of 
    487                         // the second sequence as optional 
    488                         else if ((first[i] == -1) && (second[i] != -1)) { 
    489                                 final IOptional optional = taskFactory.createNewOptional(); 
    490                                 appData.newTaskCreated(optional); 
    491                                 taskBuilder.setMarkedTask(optional, appData.getNumber2Task() 
    492                                                 .get(second[i])); 
    493                                 taskBuilder.addChild(sequence, optional); 
    494                         } 
    495                         // We have a gap in the second sequence, we need to add the task of 
    496                         // the first sequence as optional 
    497                         else if ((first[i] != -1) && (second[i] == -1)) { 
    498                                 final IOptional optional = taskFactory.createNewOptional(); 
    499                                 appData.newTaskCreated(optional); 
    500                                 taskBuilder.setMarkedTask(optional, appData.getNumber2Task() 
    501                                                 .get(first[i])); 
    502                                 taskBuilder.addChild(sequence, optional); 
    503                         } 
    504                         // Both tasks are not equal, we need to insert a selection here. 
    505                         // Now things get complicated. We first need to check 
    506                         // if the next position is not a selection. Then we can just create 
    507                         // a selection 
    508                         // of the both Tasks 
    509                         // In the other case (more than one selection following this 
    510                         // selection), we want to 
    511                         // create a selection of sequences where each sequence gets the 
    512                         // corresponding task of 
    513                         // the its sequence in the pattern. 
    514                         // 
    515                         else if (i < (first.length - 1)) { 
    516  
    517                                 if ((first[i] != second[i]) 
    518                                                 && (((first[i + 1] == second[i + 1]) 
    519                                                                 || (first[i + 1] == -1) || (second[i + 1] == -1)))) { 
    520  
    521                                         final ISelection selection = taskFactory 
    522                                                         .createNewSelection(); 
    523                                         appData.newTaskCreated(selection); 
    524                                         taskBuilder.addChild(selection, appData.getNumber2Task() 
    525                                                         .get(first[i])); 
    526                                         taskBuilder.addChild(selection, appData.getNumber2Task() 
    527                                                         .get(second[i])); 
    528                                         taskBuilder.addChild(sequence, selection); 
    529                                 } else { 
    530                                         boolean selectionfound = true; 
    531                                         final ISelection selection = taskFactory 
    532                                                         .createNewSelection(); 
    533                                         appData.newTaskCreated(selection); 
    534  
    535                                         final ISequence subsequence1 = taskFactory 
    536                                                         .createNewSequence(); 
    537                                         appData.newTaskCreated(subsequence1); 
    538  
    539                                         final ISequence subsequence2 = taskFactory 
    540                                                         .createNewSequence(); 
    541                                         appData.newTaskCreated(subsequence2); 
    542  
    543                                         taskBuilder.addChild(selection, subsequence1); 
    544                                         taskBuilder.addChild(selection, subsequence2); 
    545                                         taskBuilder.addChild(sequence, selection); 
    546                                         // TODO: We may not run till the end! 
    547                                         while ((i < (first.length - 1)) && selectionfound) { 
    548                                                 selectionfound = false; 
    549                                                 taskBuilder.addChild(subsequence1, appData 
    550                                                                 .getNumber2Task().get(first[i])); 
    551                                                 taskBuilder.addChild(subsequence2, appData 
    552                                                                 .getNumber2Task().get(second[i])); 
    553                                                 if ((first[i + 1] != second[i + 1]) 
    554                                                                 && (first[i + 1] != -1) 
    555                                                                 && (second[i + 1] != -1)) { 
    556                                                         selectionfound = true; 
    557                                                 } else { 
    558                                                         continue; 
    559                                                 } 
    560                                                 i++; 
    561                                         } 
    562                                         if ((i == (first.length - 1)) && selectionfound) { 
    563                                                 taskBuilder.addChild(subsequence1, appData 
    564                                                                 .getNumber2Task().get(first[i])); 
    565                                                 taskBuilder.addChild(subsequence2, appData 
    566                                                                 .getNumber2Task().get(second[i])); 
    567                                         } 
    568                                 } 
    569                         } else { 
    570                                 // i = length-1 
    571                                 if ((first[i] != second[i])) { 
    572  
    573                                         final ISelection selection = taskFactory 
    574                                                         .createNewSelection(); 
    575                                         appData.newTaskCreated(selection); 
    576                                         taskBuilder.addChild(selection, appData.getNumber2Task() 
    577                                                         .get(first[i])); 
    578                                         taskBuilder.addChild(selection, appData.getNumber2Task() 
    579                                                         .get(second[i])); 
    580                                         taskBuilder.addChild(sequence, selection); 
    581                                 } 
    582                         } 
    583  
    584                 } 
    585                 return sequence; 
    586         } 
    587  
    588         /** 
    589          * <p> 
    590          * replaces all occurrences of all tasks provided in the set with iterations 
    591          * </p> 
    592          * . 
    593          * 
    594          * @param iteratedTasks 
    595          *            the tasks to be replaced with iterations 
    596          * @param sessions 
    597          *            the sessions in which the tasks are to be replaced 
    598          * @param appData 
    599          *            the rule application data combining all data used for applying 
    600          *            this rule 
    601          */ 
    602         private void replaceIterationsOf(Set<ITask> iteratedTasks, 
    603                         List<IUserSession> sessions, RuleApplicationData appData) { 
    604                 final Map<ITask, IIteration> iterations = new HashMap<ITask, IIteration>(); 
    605                 final Map<IIteration, List<IIterationInstance>> iterationInstances = new HashMap<IIteration, List<IIterationInstance>>(); 
    606  
    607                 for (final ITask iteratedTask : iteratedTasks) { 
    608  
    609                         final IIteration iteration = taskFactory.createNewIteration(); 
    610                         appData.newTaskCreated(iteration); 
    611                         iterations.put(iteratedTask, iteration); 
    612                         iterationInstances.put(iteration, 
    613                                         new LinkedList<IIterationInstance>()); 
    614                 } 
    615  
    616                 IIterationInstance iterationInstance; 
    617  
    618                 for (final IUserSession session : sessions) { 
    619                         int index = 0; 
    620                         iterationInstance = null; 
    621  
    622                         while (index < session.size()) { 
    623                                 // we prepared the task instances to refer to unique tasks, if 
    624                                 // they are treated 
    625                                 // as equal. Therefore, we just compare the identity of the 
    626                                 // tasks of the task 
    627                                 // instances 
    628                                 final ITask currentTask = session.get(index).getTask(); 
    629                                 final IIteration iteration = iterations.get(currentTask); 
    630                                 if (iteration != null) { 
    631                                         if ((iterationInstance == null) 
    632                                                         || (iterationInstance.getTask() != iteration)) { 
    633                                                 iterationInstance = taskFactory 
    634                                                                 .createNewTaskInstance(iteration); 
    635                                                 iterationInstances.get(iteration) 
    636                                                                 .add(iterationInstance);// TODO:: Don't create 
    637                                                 // TaskInstances here, 
    638                                                 // use a set of tasks 
    639                                                 // instead 
    640                                                 taskBuilder.addTaskInstance(session, index, 
    641                                                                 iterationInstance); 
    642                                                 index++; 
    643                                         } 
    644  
    645                                         taskBuilder.addChild(iterationInstance, session.get(index)); 
    646                                         taskBuilder.removeTaskInstance(session, index); 
    647                                 } else { 
    648                                         if (iterationInstance != null) { 
    649                                                 iterationInstance = null; 
    650                                         } 
    651                                         index++; 
    652                                 } 
    653                         } 
    654                 } 
    655  
    656                 for (final Map.Entry<IIteration, List<IIterationInstance>> entry : iterationInstances 
    657                                 .entrySet()) { 
    658                         harmonizeIterationInstancesModel(entry.getKey(), entry.getValue()); 
    659                 } 
    660         } 
    661  
    662         private void prepareReplacements(RuleApplicationData appData) { 
    663                 appData.initializeQueues(appData.getSessions().size()); 
    664                  
    665                 for (Iterator<Match> it = appData.getMatchseqs().iterator(); it 
    666                                 .hasNext();) { 
    667                         Match m = it.next(); 
    668                         for (Iterator<MatchOccurrence> jt = m.getOccurences().iterator(); jt 
    669                                         .hasNext();) { 
    670                                 MatchOccurrence mo = jt.next(); 
    671  
    672                                 Match emptyMatch = null; 
    673                                 try { 
    674                                         emptyMatch = m.cloneWithoutOccurences(); 
    675                                 } catch (CloneNotSupportedException e) { 
    676                                         e.printStackTrace(); 
    677                                 } 
    678                                 emptyMatch.addOccurence(mo); 
    679                                 appData.getPlannedReplacements()[mo.getSequenceId()].add(m); 
    680                         } 
    681                 } 
    682         } 
    683  
    684         /** 
    685          * Replace matches. 
    686          * 
    687          * @param appData 
    688          *            the app data 
    689          */ 
    690         private void replaceMatches(RuleApplicationData appData) { 
    691          
    692                 final int numberSeqSize = appData.getNumberSequences().size(); 
    693                 Console.traceln(Level.INFO, "replacing matches with " + nThreads + " threads"); 
    694                 int newThreads = nThreads; 
    695                 if (numberSeqSize < nThreads) { 
    696                         newThreads = numberSeqSize; 
    697                 } 
    698                 final int interval = numberSeqSize / newThreads; 
    699                 int rest = numberSeqSize % newThreads; 
    700                 final ExecutorService executor = Executors.newFixedThreadPool(nThreads); 
    701                 for (int i = 0; i <= (numberSeqSize - interval); i += interval) { 
    702                         int offset = 0; 
    703                         if (rest != 0) { 
    704                                 offset = 1; 
    705                                 rest--; 
    706                         } 
    707                         final int from = i; 
    708                         final int to = i + interval + offset - 1; 
    709                         Console.traceln(Level.FINE, 
    710                                         "Match replaceing: Creating thread with matches from " + from 
    711                                                         + " to " + to); 
    712                         // search each match in every other sequence 
    713                         final ParallelMatchReplacer replacer = new ParallelMatchReplacer( 
    714                                         appData, from, to); 
    715                         executor.execute(replacer); 
    716                         i += offset; 
    717                 } 
    718                 executor.shutdown(); 
    719                 try { 
    720                         executor.awaitTermination(2, TimeUnit.HOURS); 
    721                 } catch (final InterruptedException e) { 
    722                         e.printStackTrace(); 
    723                 } 
    724         } 
    725  
    726         /** 
    727          * <p> 
    728          * searches the provided sessions for task iterations. If a task is 
    729          * iterated, it is added to the returned set. 
    730          * </p> 
    731          * 
    732          * @param sessions 
    733          *            the sessions 
    734          * @return a set of tasks being iterated somewhere 
    735          */ 
    736         private Set<ITask> searchIteratedTasks(List<IUserSession> sessions) { 
    737                 final Set<ITask> iteratedTasks = new HashSet<ITask>(); 
    738                 for (final IUserSession session : sessions) { 
    739                         for (int i = 0; i < (session.size() - 1); i++) { 
    740                                 // we prepared the task instances to refer to unique tasks, if 
    741                                 // they are treated 
    742                                 // as equal. Therefore, we just compare the identity of the 
    743                                 // tasks of the task 
    744                                 // instances 
    745                                 if (session.get(i).getTask() == session.get(i + 1).getTask()) { 
    746                                         iteratedTasks.add(session.get(i).getTask()); 
    747                                 } 
    748                         } 
    749                 } 
    750                 return iteratedTasks; 
    751         } 
    752  
    753         /** 
    754          * Generate pairwise alignments. 
    755          * 
    756          * @param appData 
    757          *            the app data 
    758          */ 
    759         private void generatePairwiseAlignments(RuleApplicationData appData) { 
    760                 final int numberSeqSize = appData.getNumberSequences().size(); 
    761                 appData.matchseqs = new LinkedList<Match>(); 
    762                 Console.traceln(Level.INFO, "generating pairwise alignments from " 
    763                                 + numberSeqSize + " sessions with " + nThreads + " threads"); 
    764  
    765                 int newThreads = nThreads; 
    766                 if (numberSeqSize < nThreads) { 
    767                         newThreads = numberSeqSize; 
    768                 } 
    769  
    770                 final ExecutorService executor = Executors 
    771                                 .newFixedThreadPool(newThreads); 
    772                 final int interval = numberSeqSize / newThreads; 
    773                 int rest = numberSeqSize % newThreads; 
    774                 Console.traceln(Level.FINE, "Interval: " + interval + " Rest: " + rest); 
    775                 for (int i = 0; i <= (numberSeqSize - interval); i += interval) { 
    776                         int offset = 0; 
    777                         if (rest != 0) { 
    778                                 offset = 1; 
    779                                 rest--; 
    780                         } 
    781  
    782                         final int from = i; 
    783                         final int to = i + interval + offset - 1; 
    784                         Console.traceln(Level.FINER, 
    785                                         "Aligning: Creating thread for sessions " + from + " till " 
    786                                                         + to); 
    787                         final ParallelPairwiseAligner aligner = new ParallelPairwiseAligner( 
    788                                         appData, from, to); 
    789                         executor.execute(aligner); 
    790                         i += offset; 
    791                 } 
    792                 executor.shutdown(); 
    793                 try { 
    794                         executor.awaitTermination(2, TimeUnit.HOURS); 
    795                 } catch (final InterruptedException e) { 
    796                         e.printStackTrace(); 
    797                 } 
    798         } 
    799  
    800         /** 
    801          * Search matches in all sessions. 
    802          * 
    803          * @param appData 
    804          *            the app data 
    805          */ 
    806         private void searchMatchesInAllSessions(RuleApplicationData appData) { 
    807  
    808                 // Prepare parallel search of matchseqs 
    809                 final int matchSeqSize = appData.getMatchseqs().size(); 
    810                 Console.traceln(Level.INFO, "searching for patterns (" + matchSeqSize 
    811                                 + ") occuring most with " + nThreads + " threads"); 
    812                 int newThreads = nThreads; 
    813                 if (matchSeqSize < nThreads) { 
    814                         newThreads = matchSeqSize; 
    815                 } 
    816                 final int interval = matchSeqSize / newThreads; 
    817                 int rest = matchSeqSize % newThreads; 
    818                 final ExecutorService executor = Executors.newFixedThreadPool(nThreads); 
    819                 Console.traceln(Level.FINER, "Interval: " + interval + " Rest: " + rest); 
    820                 for (int i = 0; i <= (matchSeqSize - interval); i += interval) { 
    821                         int offset = 0; 
    822                         if (rest != 0) { 
    823                                 offset = 1; 
    824                                 rest--; 
    825                         } 
    826                         final int from = i; 
    827                         final int to = i + interval + offset - 1; 
    828                         Console.traceln(Level.FINE, 
    829                                         "Match finding: Creating thread with matches from " + from 
    830                                                         + " to " + to); 
    831                         // search each match in every other sequence 
    832                         final ParallelMatchOcurrencesFinder finder = new ParallelMatchOcurrencesFinder( 
    833                                         appData, from, to); 
    834                         executor.execute(finder); 
    835                         i += offset; 
    836                 } 
    837                 executor.shutdown(); 
    838                 try { 
    839                         executor.awaitTermination(2, TimeUnit.HOURS); 
    840                 } catch (final InterruptedException e) { 
    841                         e.printStackTrace(); 
    842                 } 
    843  
    844         } 
    845  
    846         /* 
    847          * (non-Javadoc) 
    848          *  
    849          * @see java.lang.Object#toString() 
    850          */ 
    851         @Override 
    852         public String toString() { 
    853                 return "SequenceForTaskDetectionRuleAlignment"; 
    854         } 
    855  
    856         /** 
    857          * The Class ParallelMatchOcurrencesFinder. 
    858          */ 
    859         private class ParallelMatchOcurrencesFinder implements Runnable { 
    860  
    861                 /** The app data. */ 
    862                 private final RuleApplicationData appData; 
    863  
    864                 /** The from. */ 
    865                 private final int from; 
    866  
    867                 /** The to. */ 
    868                 private final int to; 
    869  
    870                 /** 
    871                  * Instantiates a new parallel match ocurrences finder. 
    872                  * 
    873                  * @param appData 
    874                  *            the app data 
    875                  * @param from 
    876                  *            the from 
    877                  * @param to 
    878                  *            the to 
    879                  */ 
    880                 ParallelMatchOcurrencesFinder(RuleApplicationData appData, int from, 
    881                                 int to) { 
    882                         this.appData = appData; 
    883                         this.from = from; 
    884                         this.to = to; 
    885                 } 
    886  
    887                 /* 
    888                  * (non-Javadoc) 
    889                  *  
    890                  * @see java.lang.Runnable#run() 
    891                  */ 
    892                 @Override 
    893                 public void run() { 
    894                         int count = 0; 
    895                         final int size = to + 1 - from; 
    896  
    897                         for (int i = from; i <= to; i++) { 
    898                                 final Match pattern = appData.getMatchseqs().get(i); 
    899                                 count++; 
    900                                 RuleUtils.printProgressPercentage("Match finding progress", 
    901                                                 count, size); 
    902                                 // Skip sequences with more 0 events (scrolls) than other 
    903                                 // events. 
    904                                 // Both of the pattern sequences are equally long, so the zero 
    905                                 // counts just need to be smaller than the length of one 
    906                                 // sequence 
    907                                 if ((pattern.getFirstSequence().eventCount(0) 
    908                                                 + pattern.getSecondSequence().eventCount(0) + 1) > pattern 
    909                                                 .getFirstSequence().size()) { 
    910                                         continue; 
    911                                 } 
    912  
    913                                 for (int j = 0; j < appData.getNumberSequences().size(); j++) { 
    914                                         final LinkedList<Integer> startpositions = appData 
    915                                                         .getNumberSequences().get(j) 
    916                                                         .containsPattern(pattern); 
    917                                         if (startpositions.size() > 0) { 
    918                                                 for (final Iterator<Integer> jt = startpositions 
    919                                                                 .iterator(); jt.hasNext();) { 
    920                                                         final int start = jt.next(); 
    921                                                         pattern.addOccurence(new MatchOccurrence(start, 
    922                                                                         start + pattern.size(), j)); 
    923                                                 } 
    924                                         } 
    925                                 } 
    926                         } 
    927                 } 
    928         } 
    929  
    930         /** 
    931          * The Class ParallelPairwiseAligner. 
    932          */ 
    933         private class ParallelPairwiseAligner implements Runnable { 
    934  
    935                 /** The app data. */ 
    936                 private final RuleApplicationData appData; 
    937  
    938                 /** The from. */ 
    939                 private final int from; 
    940  
    941                 /** The to. */ 
    942                 private final int to; 
    943  
    944                 /** 
    945                  * Instantiates a new parallel pairwise aligner. 
    946                  * 
    947                  * @param appData 
    948                  *            the app data 
    949                  * @param from 
    950                  *            the from 
    951                  * @param to 
    952                  *            the to 
    953                  */ 
    954                 ParallelPairwiseAligner(RuleApplicationData appData, int from, int to) { 
    955                         this.appData = appData; 
    956                         this.from = from; 
    957                         this.to = to; 
    958                 } 
    959  
    960                 /* 
    961                  * (non-Javadoc) 
    962                  *  
    963                  * @see java.lang.Runnable#run() 
    964                  */ 
    965                 @Override 
    966                 public void run() { 
    967                         int count = 0; 
    968                         final int size = to +1 - from; 
    969  
    970                         for (int i = from; i <= to; i++) { 
    971                                 final NumberSequence ns1 = appData.getNumberSequences().get(i); 
    972                                 count++; 
    973                                 RuleUtils.printProgressPercentage("Aligning Progress", count, 
    974                                                 size); 
    975                                 for (int j = 0; j < appData.getNumberSequences().size(); j++) { 
    976                                         final NumberSequence ns2 = appData.getNumberSequences() 
    977                                                         .get(j); 
    978                                         if (i != j) { 
    979                                                 final AlignmentAlgorithm aa = AlignmentAlgorithmFactory 
    980                                                                 .create(); 
    981                                                 aa.align(ns1, ns2, appData.getSubmat(), 9); 
    982                                                 synchronized (appData.getMatchseqs()) { 
    983                                                         appData.getMatchseqs().addAll(aa.getMatches()); 
    984                                                 } 
    985                                         } 
    986                                 } 
    987                         } 
    988                 } 
    989         } 
    990  
    991         /** 
    992          * The Class ParallelPairwiseAligner. 
    993          */ 
    994         private class ParallelMatchReplacer implements Runnable { 
    995  
    996                 /** The app data. */ 
    997                 private final RuleApplicationData appData; 
    998  
    999                 /** The from. */ 
    1000                 private final int from; 
    1001  
    1002                 /** The to. */ 
    1003                 private final int to; 
    1004  
    1005                 /** 
    1006                  * Instantiates a new parallel pairwise aligner. 
    1007                  * 
    1008                  * @param appData 
    1009                  *            the app data 
    1010                  * @param from 
    1011                  *            the from 
    1012                  * @param to 
    1013                  *            the to 
    1014                  */ 
    1015                 ParallelMatchReplacer(RuleApplicationData appData, int from, int to) { 
    1016                         this.appData = appData; 
    1017                         this.from = from; 
    1018                         this.to = to; 
    1019                 } 
    1020  
    1021                 /* 
    1022                  * (non-Javadoc) 
    1023                  *  
    1024                  * @see java.lang.Runnable#run() 
    1025                  */ 
    1026                 @Override 
    1027                 public void run() { 
    1028                         for (int i = from; i <= to; i++) { 
    1029                                  
    1030                                 /* 
    1031                                  * HashMap for keeping track in which sequence which replacement has 
    1032                                  * been performed. Neccessary for updating the indices of other 
    1033                                  * occurrences accordingly 
    1034                                  */ 
    1035                                 LinkedList<MatchOccurrence> replacedOccurrences = new LinkedList<MatchOccurrence>(); 
    1036                                 invalidOccurence: while (!appData.getPlannedReplacements()[i] 
    1037                                                 .isEmpty()) { 
    1038  
    1039                                         Match m = appData.getPlannedReplacements()[i].remove(); 
    1040                                         // Occurrences list has just one child 
    1041                                         MatchOccurrence oc = m.getOccurences().getFirst(); 
    1042                                         // check if we have any replaced occurrence with 
    1043                                         // indexes 
    1044                                         // smaller than ours. If so, we need to adjust 
    1045                                         // our start and end points of the replacement. 
    1046                                         // Also do a check if we have replaced this 
    1047                                         // specific MatchOccurence in this sequence already. 
    1048                                         // Jump to the next occurrence if this is the case. 
    1049                                         // This is no more necessary once the matches 
    1050                                         // are harmonized. 
    1051  
    1052                                         for (final Iterator<MatchOccurrence> jt = replacedOccurrences 
    1053                                                         .iterator(); jt.hasNext();) { 
    1054                                                 final MatchOccurrence tmpOC = jt.next(); 
    1055  
    1056                                                 if ((oc.getStartindex() >= tmpOC.getStartindex()) 
    1057                                                                 && (oc.getStartindex() <= tmpOC.getEndindex())) { 
    1058                                                         continue invalidOccurence; 
    1059                                                 } 
    1060                                                 if (oc.getEndindex() >= tmpOC.getStartindex()) { 
    1061                                                         continue invalidOccurence; 
    1062  
    1063                                                 } else if (oc.getStartindex() > tmpOC.getEndindex()) { 
    1064                                                         final int diff = tmpOC.getEndindex() 
    1065                                                                         - tmpOC.getStartindex(); 
    1066                                                         // Just to be sure. 
    1067                                                         if (diff > 0) { 
    1068                                                                 oc.setStartindex((oc.getStartindex() - diff) + 1); 
    1069                                                                 oc.setEndindex((oc.getEndindex() - diff) + 1); 
    1070                                                         } else { 
    1071                                                                 Console.traceln(Level.WARNING, 
    1072                                                                                 "End index of a Match before start. This should never happen"); 
    1073                                                         } 
    1074                                                 } 
    1075                                         } 
    1076                                         synchronized (appData) { 
    1077                                                 appData.detectedAndReplacedTasks = true; 
    1078                                         } 
    1079                                         final ISequence task = matchAsSequence(appData, m); 
    1080                                         final ISequenceInstance sequenceInstances = RuleUtils 
    1081                                                         .createNewSubSequenceInRange(appData.getSessions() 
    1082                                                                         .get(oc.getSequenceId()), oc 
    1083                                                                         .getStartindex(), oc.getEndindex(), task, 
    1084                                                                         taskFactory, taskBuilder); 
    1085  
    1086                                         // Adjust the length of the match regarding to the 
    1087                                         // length of 
    1088                                         // instance. (OptionalInstances may be shorter) 
    1089                                         oc.setEndindex((oc.getStartindex() + sequenceInstances 
    1090                                                         .size()) - RuleUtils.missedOptionals); 
    1091  
    1092                                         replacedOccurrences.add(oc); 
    1093                                 } 
    1094                         } 
    1095                 } 
    1096         } 
    1097  
    1098         /** 
    1099          * The Class RuleApplicationData. 
    1100          */ 
    1101         private static class RuleApplicationData implements Serializable { 
    1102  
    1103                 /** The Constant serialVersionUID. */ 
    1104                 private static final long serialVersionUID = -7559657686755522960L; 
    1105  
    1106                 /** 
    1107                  * The number2task HashMap. Since we align the tasks just by their 
    1108                  * integer id, we need this to convert them back to Tasks again 
    1109                  */ 
    1110                 private final HashMap<Integer, ITask> number2task; 
    1111  
    1112                 /** 
    1113                  * The unique tasks, keeps track about all unique tasks TODO: We 
    1114                  * Actually just need number2task here, this structure can be removed in 
    1115                  * the future. 
    1116                  */ 
    1117                 private final HashSet<ITask> uniqueTasks; 
    1118  
    1119                 /** 
    1120                  * The substitution matrix used by the alignment algorithm to be able to 
    1121                  * compare distances of tasks 
    1122                  */ 
    1123                 private final ObjectDistanceSubstitionMatrix submat; 
    1124  
    1125                 private PriorityQueue<Match>[] plannedReplacements; 
    1126  
    1127                 /** The list of all found matches */ 
    1128                 private LinkedList<Match> matchseqs; 
    1129  
    1130                 /** 
    1131                  * The generated NumberSequences. This is the integer representation of 
    1132                  * the user sessions 
    1133                  */ 
    1134                 private ArrayList<NumberSequence> numberseqs; 
    1135  
    1136                 /** The list of newly created tasks (of one iteration). */ 
    1137                 private final LinkedList<ITask> newTasks; 
    1138  
    1139                 /** The user sessions containing all EventTasks/Instances */ 
    1140                 private final List<IUserSession> sessions; 
    1141  
    1142                 /** True if we replaced something in the user sessions in one iteration. */ 
    1143                 private boolean detectedAndReplacedTasks; 
    1144  
    1145                 /** The result we return from this rule */ 
    1146                 private final RuleApplicationResult result; 
    1147  
    1148                 /** Stop Watch to measure performance */ 
    1149                 private final StopWatch stopWatch; 
    1150  
    1151                 /** 
    1152                  * Instantiates a new rule application data. 
    1153                  * 
    1154                  * @param sessions 
    1155                  *            The user sessions 
    1156                  */ 
    1157                 private RuleApplicationData(List<IUserSession> sessions) { 
    1158                         this.sessions = sessions; 
    1159                         numberseqs = new ArrayList<NumberSequence>(); 
    1160                         uniqueTasks = new HashSet<ITask>(); 
    1161                         number2task = new HashMap<Integer, ITask>(); 
    1162                         stopWatch = new StopWatch(); 
    1163                         result = new RuleApplicationResult(); 
    1164                         submat = new ObjectDistanceSubstitionMatrix(6, -3, false); 
    1165                         newTasks = new LinkedList<ITask>(); 
    1166                         this.detectedAndReplacedTasks = true; 
    1167                 } 
    1168  
    1169                 private void initializeQueues(int size) { 
    1170                         plannedReplacements = new PriorityQueue[size]; 
    1171                         for (int i = 0; i < size; i++) { 
    1172                                 plannedReplacements[i] = new PriorityQueue<Match>(); 
    1173                         } 
    1174                 } 
    1175  
    1176                 public Queue<Match>[] getPlannedReplacements() { 
    1177                         return plannedReplacements; 
    1178                 } 
    1179  
    1180                 /** 
    1181                  * Detected and replaced tasks. 
    1182                  * 
    1183                  * @return true, if successful 
    1184                  */ 
    1185                 private boolean detectedAndReplacedTasks() { 
    1186                         return detectedAndReplacedTasks; 
    1187                 } 
    1188  
    1189                 /** 
    1190                  * Gets the match sequences. 
    1191                  * 
    1192                  * @return the match sequences 
    1193                  */ 
    1194                 public LinkedList<Match> getMatchseqs() { 
    1195                         return matchseqs; 
    1196                 } 
    1197  
    1198                 /** 
    1199                  * Gets the new tasks. 
    1200                  * 
    1201                  * @return the new tasks 
    1202                  */ 
    1203                 public LinkedList<ITask> getNewTasks() { 
    1204                         return newTasks; 
    1205                 } 
    1206  
    1207                 /** 
    1208                  * Gets the number2task. 
    1209                  * 
    1210                  * @return the number2task HashMap 
    1211                  */ 
    1212                 private HashMap<Integer, ITask> getNumber2Task() { 
    1213                         return number2task; 
    1214                 } 
    1215  
    1216                 /** 
    1217                  * Gets the number sequences. 
    1218                  * 
    1219                  * @return the number sequences 
    1220                  */ 
    1221                 private ArrayList<NumberSequence> getNumberSequences() { 
    1222                         return numberseqs; 
    1223                 } 
    1224  
    1225                 /** 
    1226                  * Gets the result. 
    1227                  * 
    1228                  * @return the result 
    1229                  */ 
    1230                 private RuleApplicationResult getResult() { 
    1231                         return result; 
    1232                 } 
    1233  
    1234                 /** 
    1235                  * Gets the sessions. 
    1236                  * 
    1237                  * @return the UserSessions as List. 
    1238                  */ 
    1239                 private List<IUserSession> getSessions() { 
    1240                         return sessions; 
    1241                 } 
    1242  
    1243                 /** 
    1244                  * Gets the stop watch. 
    1245                  * 
    1246                  * @return the stopWatch 
    1247                  */ 
    1248                 private StopWatch getStopWatch() { 
    1249                         return stopWatch; 
    1250                 } 
    1251  
    1252                 /** 
    1253                  * Gets the substitution matrix. 
    1254                  * 
    1255                  * @return the substitution matrix 
    1256                  */ 
    1257                 private ObjectDistanceSubstitionMatrix getSubmat() { 
    1258                         return submat; 
    1259                 } 
    1260  
    1261                 /** 
    1262                  * Gets the unique tasks. 
    1263                  * 
    1264                  * @return the unique tasks 
    1265                  */ 
    1266                 private HashSet<ITask> getUniqueTasks() { 
    1267                         return uniqueTasks; 
    1268                 } 
    1269  
    1270                 /** 
    1271                  * New task created. 
    1272                  * 
    1273                  * @param task 
    1274                  *            can be called when new tasks are created to keep track of 
    1275                  *            all newly created tasks 
    1276                  */ 
    1277                 private void newTaskCreated(ITask task) { 
    1278                         number2task.put(task.getId(), task); 
    1279                         newTasks.add(task); 
    1280                 } 
    1281  
    1282                 /** 
    1283                  * Reset newly created tasks. 
    1284                  */ 
    1285                 synchronized private void resetNewlyCreatedTasks() { 
    1286                         uniqueTasks.addAll(newTasks); 
    1287                         newTasks.clear(); 
    1288                 } 
    1289  
    1290                 /** 
    1291                  * Sets the number sequences. 
    1292                  * 
    1293                  * @param numberseqs 
    1294                  *            the new number sequences 
    1295                  */ 
    1296                 private void setNumberSequences(ArrayList<NumberSequence> numberseqs) { 
    1297                         this.numberseqs = numberseqs; 
    1298                 } 
    1299  
    1300                 /** 
    1301                  * Update substitution matrix. 
    1302                  */ 
    1303                 private void updateSubstitutionMatrix() { 
    1304                         submat.update(getNewTasks()); 
    1305                         resetNewlyCreatedTasks(); 
    1306                 } 
    1307  
    1308         } 
     74    /** The n threads. */ 
     75    // public static int nThreads = Runtime.getRuntime().availableProcessors() - 1; 
     76    public static int nThreads = 1; 
     77 
     78    /** The iteration. */ 
     79    private int iteration = 0; 
     80 
     81    private int maxIterations = 1; 
     82 
     83    private static int alignmentThreshold = 9; 
     84    private static int gapPenalty = -3; 
     85 
     86    private static float maxSubstitutionScore = 6; 
     87 
     88    /** 
     89     * <p> 
     90     * the task factory to be used for creating substructures for the temporal relationships 
     91     * identified during rul application 
     92     * </p> 
     93     * . 
     94     */ 
     95    private final ITaskFactory taskFactory; 
     96 
     97    /** 
     98     * <p> 
     99     * the task builder to be used for creating substructures for the temporal relationships 
     100     * identified during rule application 
     101     * </p> 
     102     * . 
     103     */ 
     104    private final ITaskBuilder taskBuilder; 
     105 
     106    /** 
     107     * <p> 
     108     * the task handling strategy to be used for comparing tasks for preparation, i.e., before the 
     109     * tasks are harmonized 
     110     * </p> 
     111     */ 
     112    private final TaskHandlingStrategy preparationTaskHandlingStrategy; 
     113 
     114    /** 
     115     * <p> 
     116     * instantiates the rule and initializes it with a task equality to be considered when comparing 
     117     * tasks as well as a task factory and builder to be used for creating task structures. 
     118     * </p> 
     119     *  
     120     * @param minimalTaskEquality 
     121     *            the task equality to be considered when comparing tasks 
     122     * @param taskFactory 
     123     *            the task factory to be used for creating substructures 
     124     * @param taskBuilder 
     125     *            the task builder to be used for creating substructures 
     126     */ 
     127 
     128    SequenceForTaskDetectionRuleAlignment(TaskEquality minimalTaskEquality, 
     129                                          ITaskFactory taskFactory, 
     130                                          ITaskBuilder taskBuilder) 
     131    { 
     132        this.taskFactory = taskFactory; 
     133        this.taskBuilder = taskBuilder; 
     134 
     135        this.preparationTaskHandlingStrategy = new TaskHandlingStrategy(minimalTaskEquality); 
     136    } 
     137 
     138    /* 
     139     * (non-Javadoc) 
     140     *  
     141     * @see de.ugoe.cs.autoquest.tasktrees.temporalrelation.ISessionScopeRule#apply (java.util.List) 
     142     */ 
     143    @Override 
     144    public RuleApplicationResult apply(List<IUserSession> sessions) { 
     145        final RuleApplicationData appData = new RuleApplicationData(sessions); 
     146 
     147        harmonizeEventTaskInstancesModel(appData); 
     148 
     149        Console.traceln(Level.INFO, "generating substitution matrix from " + 
     150            appData.getUniqueTasks().size() + " unique tasks"); 
     151        appData.getStopWatch().start("substitution matrix"); 
     152        appData.getSubmat().generate(appData.getUniqueTasks()); 
     153        appData.getStopWatch().stop("substitution matrix"); 
     154 
     155        Console.traceln(Level.INFO, "Starting main loop"); 
     156        do { 
     157            Console.traceln(Level.INFO, "Iteration Number: " + iteration); 
     158            iteration++; 
     159            appData.detectedAndReplacedTasks = false; 
     160            appData.getStopWatch().start("whole loop"); 
     161            detectAndReplaceIterations(appData); 
     162            appData.getStopWatch().start("task replacement"); 
     163            // Just does anything if the substitution matrix is created with the 
     164            // option to do so 
     165            appData.updateSubstitutionMatrix(); 
     166            detectAndReplaceTasks(appData); // 
     167            appData.getStopWatch().stop("task replacement"); 
     168            appData.getStopWatch().stop("whole loop"); 
     169            // appData.getStopWatch().dumpStatistics(System.out); 
     170            appData.getStopWatch().reset(); 
     171 
     172            // } while (appData.detectedAndReplacedTasks()||iteration < maxIterations); 
     173        } 
     174        while (iteration < maxIterations); 
     175        Console.println("created " + appData.getResult().getNewlyCreatedTasks().size() + 
     176            " new tasks and " + appData.getResult().getNewlyCreatedTaskInstances().size() + 
     177            " appropriate instances\n"); 
     178 
     179        if ((appData.getResult().getNewlyCreatedTasks().size() > 0) || 
     180            (appData.getResult().getNewlyCreatedTaskInstances().size() > 0)) 
     181        { 
     182            appData.getResult().setRuleApplicationStatus(RuleApplicationStatus.FINISHED); 
     183        } 
     184        // new TaskTreeValidator().validate(appData.getSessions()); 
     185        return appData.getResult(); 
     186    } 
     187 
     188    /** 
     189     * Creates the number sequences. 
     190     * 
     191     * @param appData 
     192     *            the app data 
     193     * @return the array list 
     194     */ 
     195    private ArrayList<NumberSequence> createNumberSequences(RuleApplicationData appData) { 
     196        final ArrayList<NumberSequence> result = new ArrayList<NumberSequence>(); 
     197        for (int i = 0; i < appData.getSessions().size(); i++) { 
     198            final IUserSession session = appData.getSessions().get(i); 
     199            final NumberSequence templist = new NumberSequence(session.size()); 
     200            for (int j = 0; j < session.size(); j++) { 
     201                final ITaskInstance taskInstance = session.get(j); 
     202                templist.getSequence()[j] = taskInstance.getTask().getId(); 
     203            } 
     204            // Each NumberSequence is identified by its id, beginning to count 
     205            // at zero 
     206            templist.setId(i); 
     207            result.add(templist); 
     208        } 
     209        return result; 
     210    } 
     211 
     212    /** 
     213     * <p> 
     214     * searches for direct iterations of single tasks in all sequences and replaces them with 
     215     * {@link IIteration}s, respectively appropriate instances. Also all single occurrences of a 
     216     * task that is iterated somewhen are replaced with iterations to have again an efficient way 
     217     * for task comparisons. 
     218     * </p> 
     219     *  
     220     * @param appData 
     221     *            the rule application data combining all data used for applying this rule 
     222     */ 
     223    private void detectAndReplaceIterations(RuleApplicationData appData) { 
     224        Console.traceln(Level.FINE, "detecting iterations"); 
     225        appData.getStopWatch().start("detecting iterations"); 
     226 
     227        final List<IUserSession> sessions = appData.getSessions(); 
     228 
     229        final Set<ITask> iteratedTasks = searchIteratedTasks(sessions); 
     230 
     231        if (iteratedTasks.size() > 0) { 
     232            replaceIterationsOf(iteratedTasks, sessions, appData); 
     233        } 
     234 
     235        appData.getStopWatch().stop("detecting iterations"); 
     236        Console.traceln(Level.INFO, "replaced " + iteratedTasks.size() + " iterated tasks"); 
     237    } 
     238 
     239    /** 
     240     * Detect and replace tasks. 
     241     * 
     242     * @param appData 
     243     *            the rule application data combining all data used for applying this rule 
     244     */ 
     245    private void detectAndReplaceTasks(RuleApplicationData appData) { 
     246        Console.traceln(Level.FINE, "detecting and replacing tasks"); 
     247        appData.getStopWatch().start("detecting tasks"); 
     248 
     249        // Create NumberSequences 
     250        appData.setNumberSequences(this.createNumberSequences(appData)); 
     251 
     252        // Generate pairwise alignments 
     253        // appData.setMatchseqs(generatePairwiseAlignments(appData)); 
     254        generatePairwiseAlignments(appData); 
     255        Console.traceln(Level.FINE, "Found " + appData.getMatchseqs().size() + " results"); 
     256 
     257        // Nothing found, we can end here 
     258        if (appData.getMatchseqs().size() == 0) { 
     259            appData.detectedAndReplacedTasks = false; 
     260            return; 
     261        } 
     262 
     263        // Searching each match in all other sessions, counting its occurences 
     264        searchMatchesInAllSessions(appData); 
     265 
     266        // Sort results to get the most occurring results 
     267        Console.traceln(Level.INFO, "sorting " + appData.getMatchseqs().size() + " results"); 
     268        final Comparator<Match> comparator = new Comparator<Match>() { 
     269            @Override 
     270            public int compare(Match m1, Match m2) { 
     271                int cmp = m2.occurenceCount() - m1.occurenceCount(); 
     272                if (cmp != 0) { 
     273                    return cmp; 
     274                } 
     275                else { 
     276                    cmp = m2.size() - m1.size(); 
     277                    if (cmp != 0) { 
     278                        return cmp; 
     279                    } 
     280                    else { 
     281                        // This should rarely happen 
     282                        cmp = m2.ocurrenceIDSum() - m1.ocurrenceIDSum(); 
     283                        if (cmp != 0) { 
     284                            return cmp; 
     285                        } 
     286                        else { 
     287                            cmp = m2.taskIdSum() - m1.taskIdSum(); 
     288 
     289                            return cmp; 
     290                        } 
     291                    } 
     292                } 
     293            } 
     294        }; 
     295 
     296        Collections.sort(appData.getMatchseqs(), comparator); 
     297        appData.getStopWatch().stop("detecting tasks"); 
     298 
     299        Console.traceln(Level.INFO, "Preparing replacments"); 
     300        prepareReplacements(appData); 
     301        Console.traceln(Level.INFO, "Replacing matches in sessions"); 
     302        appData.getStopWatch().start("replacing tasks"); 
     303        replaceMatches(appData); 
     304        appData.getStopWatch().stop("replacing tasks"); 
     305    } 
     306 
     307    // TODO: DEBUG METHOD 
     308    @SuppressWarnings("unused") 
     309    private void printMatches(RuleApplicationData appData) { 
     310        LinkedList<Match> matchseqs = appData.getMatchseqs(); 
     311        if (iteration > 1) { 
     312            System.out.println("PRINTING MATCHES"); 
     313            for (Iterator<Match> it = matchseqs.iterator(); it.hasNext();) { 
     314                Match m = it.next(); 
     315                m.getFirstSequence().printSequence(); 
     316                m.getSecondSequence().printSequence(); 
     317                for (Iterator<MatchOccurrence> jt = m.getOccurences().iterator(); jt.hasNext();) { 
     318                    MatchOccurrence mo = jt.next(); 
     319                    System.out.print(mo.getSequenceId() + " "); 
     320                } 
     321                System.out.println(); 
     322                System.out.println(); 
     323            } 
     324        } 
     325    } 
     326 
     327    /** 
     328     * <p> 
     329     * harmonizes the event task instances by unifying tasks. This is done, as initially the event 
     330     * tasks being equal with respect to the considered task equality are distinct objects. The 
     331     * comparison of these distinct objects is more time consuming than comparing the object 
     332     * references. 
     333     * </p> 
     334     *  
     335     * @param appData 
     336     *            the rule application data combining all data used for applying this rule 
     337     * @return Returns the unique tasks symbol map 
     338     */ 
     339    private void harmonizeEventTaskInstancesModel(RuleApplicationData appData) { 
     340        Console.traceln(Level.INFO, "harmonizing task model of event task instances"); 
     341        appData.getStopWatch().start("harmonizing event tasks"); 
     342        final SymbolMap<ITaskInstance, ITask> uniqueTasks = 
     343            preparationTaskHandlingStrategy.createSymbolMap(); 
     344 
     345        final TaskInstanceComparator comparator = 
     346            preparationTaskHandlingStrategy.getTaskComparator(); 
     347 
     348        int unifiedTasks = 0; 
     349        ITask task; 
     350        final List<IUserSession> sessions = appData.getSessions(); 
     351        for (int j = 0; j < sessions.size(); j++) { 
     352            final IUserSession session = sessions.get(j); 
     353 
     354            for (int i = 0; i < session.size(); i++) { 
     355                final ITaskInstance taskInstance = session.get(i); 
     356                task = uniqueTasks.getValue(taskInstance); 
     357 
     358                if (task == null) { 
     359                    uniqueTasks.addSymbol(taskInstance, taskInstance.getTask()); 
     360                    appData.getUniqueTasks().add(taskInstance.getTask()); 
     361                    appData.getNumber2Task().put(taskInstance.getTask().getId(), 
     362                                                 taskInstance.getTask()); 
     363                } 
     364                else { 
     365                    taskBuilder.setTask(taskInstance, task); 
     366                    unifiedTasks++; 
     367                } 
     368            } 
     369            comparator.clearBuffers(); 
     370        } 
     371 
     372        appData.getStopWatch().stop("harmonizing event tasks"); 
     373        Console.traceln(Level.INFO, "harmonized " + unifiedTasks + " task occurrences (still " + 
     374            appData.getUniqueTasks().size() + " different tasks)"); 
     375 
     376        appData.getStopWatch().dumpStatistics(System.out); 
     377        appData.getStopWatch().reset(); 
     378    } 
     379 
     380    /** 
     381     * <p> 
     382     * TODO clarify why this is done (in fact, ask Patrick Harms) 
     383     * </p> 
     384     * . 
     385     * 
     386     * @param iteration 
     387     *            the iteration 
     388     * @param iterationInstances 
     389     *            the iteration instances 
     390     */ 
     391    private void harmonizeIterationInstancesModel(IIteration iteration, 
     392                                                  List<IIterationInstance> iterationInstances) 
     393    { 
     394        final List<ITask> iteratedTaskVariants = new LinkedList<ITask>(); 
     395        final TaskInstanceComparator comparator = 
     396            preparationTaskHandlingStrategy.getTaskComparator(); 
     397 
     398        // merge the lexically different variants of iterated task to a unique 
     399        // list 
     400        for (final IIterationInstance iterationInstance : iterationInstances) { 
     401            for (final ITaskInstance executionVariant : iterationInstance) { 
     402                final ITask candidate = executionVariant.getTask(); 
     403 
     404                boolean found = false; 
     405                for (final ITask taskVariant : iteratedTaskVariants) { 
     406                    if (comparator.areLexicallyEqual(taskVariant, candidate)) { 
     407                        taskBuilder.setTask(executionVariant, taskVariant); 
     408                        found = true; 
     409                        break; 
     410                    } 
     411                } 
     412 
     413                if (!found) { 
     414                    iteratedTaskVariants.add(candidate); 
     415                } 
     416            } 
     417        } 
     418 
     419        // if there are more than one lexically different variant of iterated 
     420        // tasks, adapt the 
     421        // iteration model to be a selection of different variants. In this case 
     422        // also adapt 
     423        // the generated iteration instances to correctly contain selection 
     424        // instances. If there 
     425        // is only one variant of an iterated task, simply set this as the 
     426        // marked task of the 
     427        // iteration. In this case, the instances can be preserved as is 
     428        if (iteratedTaskVariants.size() > 1) { 
     429            final ISelection selection = taskFactory.createNewSelection(); 
     430 
     431            for (final ITask variant : iteratedTaskVariants) { 
     432                taskBuilder.addChild(selection, variant); 
     433            } 
     434 
     435            taskBuilder.setMarkedTask(iteration, selection); 
     436 
     437            for (final IIterationInstance instance : iterationInstances) { 
     438                for (int i = 0; i < instance.size(); i++) { 
     439                    final ISelectionInstance selectionInstance = 
     440                        taskFactory.createNewTaskInstance(selection); 
     441                    taskBuilder.setChild(selectionInstance, instance.get(i)); 
     442                    taskBuilder.setTaskInstance(instance, i, selectionInstance); 
     443                } 
     444            } 
     445        } 
     446        else { 
     447            taskBuilder.setMarkedTask(iteration, iteratedTaskVariants.get(0)); 
     448        } 
     449    } 
     450 
     451    /** 
     452     * Match as sequence. 
     453     * 
     454     * @param appData 
     455     *            RuleApplicationData needed to keep track of all created tasks 
     456     * @param m 
     457     *            The match to be converted into a Task 
     458     * @return The task of the match with an ISequence as its root 
     459     */ 
     460    synchronized public ISequence matchAsSequence(RuleApplicationData appData, Match m) { 
     461        final ISequence sequence = taskFactory.createNewSequence(); 
     462        appData.newTaskCreated(sequence); 
     463 
     464        final int[] first = m.getFirstSequence().getSequence(); 
     465        final int[] second = m.getSecondSequence().getSequence(); 
     466 
     467        // Both sequences of a match are equally long 
     468        for (int i = 0; i < m.getFirstSequence().size(); i++) { 
     469 
     470            // Two gaps aligned to each other: Have not seen it happening so 
     471            // far, just to handle it 
     472            if ((first[i] == -1) && (second[i] == -1)) { 
     473                // Do nothing here. 
     474            } 
     475            // Both events are equal, we can simply add the task referring to 
     476            // the number 
     477            else if (first[i] == second[i]) { 
     478                taskBuilder.addChild(sequence, appData.getNumber2Task().get(first[i])); 
     479            } 
     480            // We have a gap in the first sequence, we need to add the task of 
     481            // the second sequence as optional 
     482            else if ((first[i] == -1) && (second[i] != -1)) { 
     483                final IOptional optional = taskFactory.createNewOptional(); 
     484                appData.newTaskCreated(optional); 
     485                taskBuilder.setMarkedTask(optional, appData.getNumber2Task().get(second[i])); 
     486                taskBuilder.addChild(sequence, optional); 
     487            } 
     488            // We have a gap in the second sequence, we need to add the task of 
     489            // the first sequence as optional 
     490            else if ((first[i] != -1) && (second[i] == -1)) { 
     491                final IOptional optional = taskFactory.createNewOptional(); 
     492                appData.newTaskCreated(optional); 
     493                taskBuilder.setMarkedTask(optional, appData.getNumber2Task().get(first[i])); 
     494                taskBuilder.addChild(sequence, optional); 
     495            } 
     496            // Both tasks are not equal, we need to insert a selection here. 
     497            // Now things get complicated. We first need to check 
     498            // if the next position is not a selection. Then we can just create 
     499            // a selection 
     500            // of the both Tasks 
     501            // In the other case (more than one selection following this 
     502            // selection), we want to 
     503            // create a selection of sequences where each sequence gets the 
     504            // corresponding task of 
     505            // the its sequence in the pattern. 
     506            // 
     507            else if (i < (first.length - 1)) { 
     508 
     509                if ((first[i] != second[i]) && 
     510                    (((first[i + 1] == second[i + 1]) || (first[i + 1] == -1) || (second[i + 1] == -1)))) 
     511                { 
     512 
     513                    final ISelection selection = taskFactory.createNewSelection(); 
     514                    appData.newTaskCreated(selection); 
     515                    taskBuilder.addChild(selection, appData.getNumber2Task().get(first[i])); 
     516                    taskBuilder.addChild(selection, appData.getNumber2Task().get(second[i])); 
     517                    taskBuilder.addChild(sequence, selection); 
     518                } 
     519                else { 
     520                    boolean selectionfound = true; 
     521                    final ISelection selection = taskFactory.createNewSelection(); 
     522                    appData.newTaskCreated(selection); 
     523 
     524                    final ISequence subsequence1 = taskFactory.createNewSequence(); 
     525                    appData.newTaskCreated(subsequence1); 
     526 
     527                    final ISequence subsequence2 = taskFactory.createNewSequence(); 
     528                    appData.newTaskCreated(subsequence2); 
     529 
     530                    taskBuilder.addChild(selection, subsequence1); 
     531                    taskBuilder.addChild(selection, subsequence2); 
     532                    taskBuilder.addChild(sequence, selection); 
     533                    // TODO: We may not run till the end! 
     534                    while ((i < (first.length - 1)) && selectionfound) { 
     535                        selectionfound = false; 
     536                        taskBuilder.addChild(subsequence1, appData.getNumber2Task().get(first[i])); 
     537                        taskBuilder.addChild(subsequence2, appData.getNumber2Task().get(second[i])); 
     538                        if ((first[i + 1] != second[i + 1]) && (first[i + 1] != -1) && 
     539                            (second[i + 1] != -1)) 
     540                        { 
     541                            selectionfound = true; 
     542                        } 
     543                        else { 
     544                            continue; 
     545                        } 
     546                        i++; 
     547                    } 
     548                    if ((i == (first.length - 1)) && selectionfound) { 
     549                        taskBuilder.addChild(subsequence1, appData.getNumber2Task().get(first[i])); 
     550                        taskBuilder.addChild(subsequence2, appData.getNumber2Task().get(second[i])); 
     551                    } 
     552                } 
     553            } 
     554            else { 
     555                // i = length-1 
     556                if ((first[i] != second[i])) { 
     557 
     558                    final ISelection selection = taskFactory.createNewSelection(); 
     559                    appData.newTaskCreated(selection); 
     560                    taskBuilder.addChild(selection, appData.getNumber2Task().get(first[i])); 
     561                    taskBuilder.addChild(selection, appData.getNumber2Task().get(second[i])); 
     562                    taskBuilder.addChild(sequence, selection); 
     563                } 
     564            } 
     565 
     566        } 
     567        return sequence; 
     568    } 
     569 
     570    /** 
     571     * <p> 
     572     * replaces all occurrences of all tasks provided in the set with iterations 
     573     * </p> 
     574     * . 
     575     * 
     576     * @param iteratedTasks 
     577     *            the tasks to be replaced with iterations 
     578     * @param sessions 
     579     *            the sessions in which the tasks are to be replaced 
     580     * @param appData 
     581     *            the rule application data combining all data used for applying this rule 
     582     */ 
     583    private void replaceIterationsOf(Set<ITask> iteratedTasks, 
     584                                     List<IUserSession> sessions, 
     585                                     RuleApplicationData appData) 
     586    { 
     587        final Map<ITask, IIteration> iterations = new HashMap<ITask, IIteration>(); 
     588        final Map<IIteration, List<IIterationInstance>> iterationInstances = 
     589            new HashMap<IIteration, List<IIterationInstance>>(); 
     590 
     591        for (final ITask iteratedTask : iteratedTasks) { 
     592 
     593            final IIteration iteration = taskFactory.createNewIteration(); 
     594            appData.newTaskCreated(iteration); 
     595            iterations.put(iteratedTask, iteration); 
     596            iterationInstances.put(iteration, new LinkedList<IIterationInstance>()); 
     597        } 
     598 
     599        IIterationInstance iterationInstance; 
     600 
     601        for (final IUserSession session : sessions) { 
     602            int index = 0; 
     603            iterationInstance = null; 
     604 
     605            while (index < session.size()) { 
     606                // we prepared the task instances to refer to unique tasks, if 
     607                // they are treated 
     608                // as equal. Therefore, we just compare the identity of the 
     609                // tasks of the task 
     610                // instances 
     611                final ITask currentTask = session.get(index).getTask(); 
     612                final IIteration iteration = iterations.get(currentTask); 
     613                if (iteration != null) { 
     614                    if ((iterationInstance == null) || (iterationInstance.getTask() != iteration)) { 
     615                        iterationInstance = taskFactory.createNewTaskInstance(iteration); 
     616                        iterationInstances.get(iteration).add(iterationInstance);// TODO:: Don't 
     617                                                                                 // create 
     618                        // TaskInstances here, 
     619                        // use a set of tasks 
     620                        // instead 
     621                        taskBuilder.addTaskInstance(session, index, iterationInstance); 
     622                        index++; 
     623                    } 
     624 
     625                    taskBuilder.addChild(iterationInstance, session.get(index)); 
     626                    taskBuilder.removeTaskInstance(session, index); 
     627                } 
     628                else { 
     629                    if (iterationInstance != null) { 
     630                        iterationInstance = null; 
     631                    } 
     632                    index++; 
     633                } 
     634            } 
     635        } 
     636 
     637        for (final Map.Entry<IIteration, List<IIterationInstance>> entry : iterationInstances 
     638            .entrySet()) 
     639        { 
     640            harmonizeIterationInstancesModel(entry.getKey(), entry.getValue()); 
     641        } 
     642    } 
     643 
     644    /** 
     645     * Prepare replacements. 
     646     * 
     647     * @param appData 
     648     *            the app data 
     649     */ 
     650    private void prepareReplacements(RuleApplicationData appData) { 
     651        appData.initializeQueues(appData.getSessions().size()); 
     652        int taskId=0; 
     653        for (Iterator<Match> it = appData.getMatchseqs().iterator(); it.hasNext();) { 
     654            Match m = it.next(); 
     655            if (m.getOccurences().size() > 2) { 
     656                final ISequence task = matchAsSequence(appData, m); 
     657                appData.getPreparedTasks().add(task); 
     658                 
     659                for (Iterator<MatchOccurrence> jt = m.getOccurences().iterator(); jt.hasNext();) { 
     660                    MatchOccurrence mo = jt.next(); 
     661                    // System.out.println("Found this match in sequence " +mo.getSequenceId()); 
     662                    PlannedReplacement pr = new PlannedReplacement(taskId,mo); 
     663                    // System.out.println("Adding match to sequence " + mo.getSequenceId()); 
     664                    appData.getPlannedReplacements()[mo.getSequenceId()].add(pr); 
     665                } 
     666                taskId++; 
     667            } 
     668        } 
     669    } 
     670 
     671    /** 
     672     * Replace matches. 
     673     * 
     674     * @param appData 
     675     *            the app data 
     676     */ 
     677    private void replaceMatches(RuleApplicationData appData) { 
     678 
     679        final int numberSeqSize = appData.getNumberSequences().size(); 
     680        Console.traceln(Level.INFO, "replacing matches with " + nThreads + " threads"); 
     681        int newThreads = nThreads; 
     682        if (numberSeqSize < nThreads) { 
     683            newThreads = numberSeqSize; 
     684        } 
     685        final int interval = numberSeqSize / newThreads; 
     686        int rest = numberSeqSize % newThreads; 
     687        final ExecutorService executor = Executors.newFixedThreadPool(nThreads); 
     688        for (int i = 0; i <= (numberSeqSize - interval); i += interval) { 
     689            int offset = 0; 
     690            if (rest != 0) { 
     691                offset = 1; 
     692                rest--; 
     693            } 
     694            final int from = i; 
     695            final int to = i + interval + offset - 1; 
     696            Console.traceln(Level.FINE, "Match replacing: Creating thread with matches from " + 
     697                from + " to " + to); 
     698            // search each match in every other sequence 
     699            final ParallelMatchReplacer replacer = new ParallelMatchReplacer(appData, from, to); 
     700            executor.execute(replacer); 
     701            i += offset; 
     702        } 
     703        executor.shutdown(); 
     704        try { 
     705            executor.awaitTermination(2, TimeUnit.HOURS); 
     706        } 
     707        catch (final InterruptedException e) { 
     708            e.printStackTrace(); 
     709        } 
     710    } 
     711 
     712    /** 
     713     * <p> 
     714     * searches the provided sessions for task iterations. If a task is iterated, it is added to the 
     715     * returned set. 
     716     * </p> 
     717     * 
     718     * @param sessions 
     719     *            the sessions 
     720     * @return a set of tasks being iterated somewhere 
     721     */ 
     722    private Set<ITask> searchIteratedTasks(List<IUserSession> sessions) { 
     723        final Set<ITask> iteratedTasks = new HashSet<ITask>(); 
     724        for (final IUserSession session : sessions) { 
     725            for (int i = 0; i < (session.size() - 1); i++) { 
     726                // we prepared the task instances to refer to unique tasks, if 
     727                // they are treated 
     728                // as equal. Therefore, we just compare the identity of the 
     729                // tasks of the task 
     730                // instances 
     731                if (session.get(i).getTask() == session.get(i + 1).getTask()) { 
     732                    iteratedTasks.add(session.get(i).getTask()); 
     733                } 
     734            } 
     735        } 
     736        return iteratedTasks; 
     737    } 
     738 
     739    /** 
     740     * Generate pairwise alignments. 
     741     * 
     742     * @param appData 
     743     *            the app data 
     744     */ 
     745    private void generatePairwiseAlignments(RuleApplicationData appData) { 
     746        final int numberSeqSize = appData.getNumberSequences().size(); 
     747        appData.matchseqs = new LinkedList<Match>(); 
     748        Console.traceln(Level.INFO, "generating pairwise alignments from " + numberSeqSize + 
     749            " sessions with " + nThreads + " threads"); 
     750 
     751        int newThreads = nThreads; 
     752        if (numberSeqSize < nThreads) { 
     753            newThreads = numberSeqSize; 
     754        } 
     755 
     756        final ExecutorService executor = Executors.newFixedThreadPool(newThreads); 
     757        final int interval = numberSeqSize / newThreads; 
     758        int rest = numberSeqSize % newThreads; 
     759        Console.traceln(Level.FINE, "Interval: " + interval + " Rest: " + rest); 
     760        for (int i = 0; i <= (numberSeqSize - interval); i += interval) { 
     761            int offset = 0; 
     762            if (rest != 0) { 
     763                offset = 1; 
     764                rest--; 
     765            } 
     766 
     767            final int from = i; 
     768            final int to = i + interval + offset - 1; 
     769            Console.traceln(Level.FINER, "Aligning: Creating thread for sessions " + from + 
     770                " till " + to); 
     771            final ParallelPairwiseAligner aligner = new ParallelPairwiseAligner(appData, from, to); 
     772            executor.execute(aligner); 
     773            i += offset; 
     774        } 
     775        executor.shutdown(); 
     776        try { 
     777            executor.awaitTermination(2, TimeUnit.HOURS); 
     778        } 
     779        catch (final InterruptedException e) { 
     780            e.printStackTrace(); 
     781        } 
     782    } 
     783 
     784    /** 
     785     * Search matches in all sessions. 
     786     * 
     787     * @param appData 
     788     *            the app data 
     789     */ 
     790    private void searchMatchesInAllSessions(RuleApplicationData appData) { 
     791 
     792        // Prepare parallel search of matchseqs 
     793        final int matchSeqSize = appData.getMatchseqs().size(); 
     794        Console.traceln(Level.INFO, "searching for patterns (" + matchSeqSize + 
     795            ") occuring most with " + nThreads + " threads"); 
     796        int newThreads = nThreads; 
     797        if (matchSeqSize < nThreads) { 
     798            newThreads = matchSeqSize; 
     799        } 
     800        final int interval = matchSeqSize / newThreads; 
     801        int rest = matchSeqSize % newThreads; 
     802        final ExecutorService executor = Executors.newFixedThreadPool(nThreads); 
     803        Console.traceln(Level.FINER, "Interval: " + interval + " Rest: " + rest); 
     804        for (int i = 0; i <= (matchSeqSize - interval); i += interval) { 
     805            int offset = 0; 
     806            if (rest != 0) { 
     807                offset = 1; 
     808                rest--; 
     809            } 
     810            final int from = i; 
     811            final int to = i + interval + offset - 1; 
     812            Console.traceln(Level.FINE, "Match finding: Creating thread with matches from " + from + 
     813                " to " + to); 
     814            // search each match in every other sequence 
     815            final ParallelMatchOcurrencesFinder finder = 
     816                new ParallelMatchOcurrencesFinder(appData, from, to); 
     817            executor.execute(finder); 
     818            i += offset; 
     819        } 
     820        executor.shutdown(); 
     821        try { 
     822            executor.awaitTermination(2, TimeUnit.HOURS); 
     823        } 
     824        catch (final InterruptedException e) { 
     825            e.printStackTrace(); 
     826        } 
     827 
     828    } 
     829 
     830    /* 
     831     * (non-Javadoc) 
     832     *  
     833     * @see java.lang.Object#toString() 
     834     */ 
     835    @Override 
     836    public String toString() { 
     837        return "SequenceForTaskDetectionRuleAlignment"; 
     838    } 
     839 
     840    /** 
     841     * The Class ParallelMatchOcurrencesFinder. 
     842     */ 
     843    private class ParallelMatchOcurrencesFinder implements Runnable { 
     844 
     845        /** The app data. */ 
     846        private final RuleApplicationData appData; 
     847 
     848        /** The from. */ 
     849        private final int from; 
     850 
     851        /** The to. */ 
     852        private final int to; 
     853 
     854        /** 
     855         * Instantiates a new parallel match ocurrences finder. 
     856         * 
     857         * @param appData 
     858         *            the app data 
     859         * @param from 
     860         *            the from 
     861         * @param to 
     862         *            the to 
     863         */ 
     864        ParallelMatchOcurrencesFinder(RuleApplicationData appData, int from, int to) { 
     865            this.appData = appData; 
     866            this.from = from; 
     867            this.to = to; 
     868        } 
     869 
     870        /* 
     871         * (non-Javadoc) 
     872         *  
     873         * @see java.lang.Runnable#run() 
     874         */ 
     875        @Override 
     876        public void run() { 
     877            int count = 0; 
     878            final int size = to + 1 - from; 
     879 
     880            for (int i = from; i <= to; i++) { 
     881                final Match pattern = appData.getMatchseqs().get(i); 
     882                count++; 
     883                RuleUtils.printProgressPercentage("Match finding progress", count, size); 
     884                // Skip sequences with more 0 events (scrolls) than other 
     885                // events. 
     886                // Both of the pattern sequences are equally long, so the zero 
     887                // counts just need to be smaller than the length of one 
     888                // sequence 
     889                if ((pattern.getFirstSequence().eventCount(0) + 
     890                    pattern.getSecondSequence().eventCount(0) + 1) > pattern.getFirstSequence() 
     891                    .size()) 
     892                { 
     893                    continue; 
     894                } 
     895 
     896                for (int j = 0; j < appData.getNumberSequences().size(); j++) { 
     897                    final LinkedList<Integer> startpositions = 
     898                        appData.getNumberSequences().get(j).containsPattern(pattern); 
     899                    if (startpositions.size() > 0) { 
     900                        for (final Iterator<Integer> jt = startpositions.iterator(); jt.hasNext();) 
     901                        { 
     902                            final int start = jt.next(); 
     903                            pattern.addOccurence(new MatchOccurrence(start, start + pattern.size(), 
     904                                                                     j)); 
     905                        } 
     906                    } 
     907                } 
     908            } 
     909        } 
     910    } 
     911 
     912    /** 
     913     * The Class ParallelPairwiseAligner. 
     914     */ 
     915    private class ParallelPairwiseAligner implements Runnable { 
     916 
     917        /** The app data. */ 
     918        private final RuleApplicationData appData; 
     919 
     920        /** The from. */ 
     921        private final int from; 
     922 
     923        /** The to. */ 
     924        private final int to; 
     925 
     926        /** 
     927         * Instantiates a new parallel pairwise aligner. 
     928         * 
     929         * @param appData 
     930         *            the app data 
     931         * @param from 
     932         *            the from 
     933         * @param to 
     934         *            the to 
     935         */ 
     936        ParallelPairwiseAligner(RuleApplicationData appData, int from, int to) { 
     937            this.appData = appData; 
     938            this.from = from; 
     939            this.to = to; 
     940        } 
     941 
     942        /* 
     943         * (non-Javadoc) 
     944         *  
     945         * @see java.lang.Runnable#run() 
     946         */ 
     947        @Override 
     948        public void run() { 
     949            int count = 0; 
     950            final int size = to + 1 - from; 
     951 
     952            for (int i = from; i <= to; i++) { 
     953                final NumberSequence ns1 = appData.getNumberSequences().get(i); 
     954                count++; 
     955                RuleUtils.printProgressPercentage("Aligning Progress", count, size); 
     956                for (int j = 0; j < appData.getNumberSequences().size(); j++) { 
     957                    final NumberSequence ns2 = appData.getNumberSequences().get(j); 
     958                    if (i != j) { 
     959                        final AlignmentAlgorithm aa = AlignmentAlgorithmFactory.create(); 
     960                        // threshold was 9 
     961                        aa.align(ns1, ns2, appData.getSubmat(), 
     962                                 SequenceForTaskDetectionRuleAlignment.alignmentThreshold); 
     963                        synchronized (appData.getMatchseqs()) { 
     964                            appData.getMatchseqs().addAll(aa.getMatches()); 
     965                        } 
     966                    } 
     967                } 
     968            } 
     969        } 
     970    } 
     971 
     972    /** 
     973     * The Class ParallelPairwiseAligner. 
     974     */ 
     975    private class ParallelMatchReplacer implements Runnable { 
     976 
     977        /** The app data. */ 
     978        private final RuleApplicationData appData; 
     979 
     980        /** The from. */ 
     981        private final int from; 
     982 
     983        /** The to. */ 
     984        private final int to; 
     985 
     986        /** 
     987         * Instantiates a new parallel pairwise aligner. 
     988         * 
     989         * @param appData 
     990         *            the app data 
     991         * @param from 
     992         *            the from 
     993         * @param to 
     994         *            the to 
     995         */ 
     996        ParallelMatchReplacer(RuleApplicationData appData, int from, int to) { 
     997            this.appData = appData; 
     998            this.from = from; 
     999            this.to = to; 
     1000        } 
     1001 
     1002        /* 
     1003         * (non-Javadoc) 
     1004         *  
     1005         * @see java.lang.Runnable#run() 
     1006         */ 
     1007        @Override 
     1008        public void run() { 
     1009            for (int i = from; i <= to; i++) { 
     1010                System.out.println("Replacing in SEQUENCE " + i); 
     1011                /* 
     1012                 * HashMap for keeping track in which sequence which replacement has been performed. 
     1013                 * Neccessary for updating the indices of other occurrences accordingly 
     1014                 */ 
     1015                LinkedList<MatchOccurrence> replacedOccurrences = new LinkedList<MatchOccurrence>(); 
     1016                invalidOccurence: while (!appData.getPlannedReplacements()[i].isEmpty()) { 
     1017 
     1018                    PlannedReplacement pr = appData.getPlannedReplacements()[i].remove(); 
     1019                     
     1020                    MatchOccurrence oc = pr.getOccurrence(); 
     1021                    // check if we have any replaced occurrence with 
     1022                    // indexes 
     1023                    // smaller than ours. If so, we need to adjust 
     1024                    // our start and end points of the replacement. 
     1025                    // Also do a check if we have replaced this 
     1026                    // specific MatchOccurence in this sequence already. 
     1027                    // Jump to the next occurrence if this is the case. 
     1028                    // This is no more necessary once the matches 
     1029                    // are harmonized. 
     1030 
     1031                    for (final Iterator<MatchOccurrence> jt = replacedOccurrences.iterator(); jt 
     1032                        .hasNext();) 
     1033                    { 
     1034                        final MatchOccurrence tmpOC = jt.next(); 
     1035 
     1036                        if ((oc.getStartindex() >= tmpOC.getStartindex()) && 
     1037                            (oc.getStartindex() <= tmpOC.getEndindex())) 
     1038                        { 
     1039                            continue invalidOccurence; 
     1040                        } 
     1041                        if (oc.getEndindex() >= tmpOC.getStartindex()) { 
     1042                            continue invalidOccurence; 
     1043 
     1044                        } 
     1045                        else if (oc.getStartindex() > tmpOC.getEndindex()) { 
     1046                            final int diff = tmpOC.getEndindex() - tmpOC.getStartindex(); 
     1047                            // Just to be sure. 
     1048                            if (diff > 0) { 
     1049                                oc.setStartindex((oc.getStartindex() - diff) + 1); 
     1050                                oc.setEndindex((oc.getEndindex() - diff) + 1); 
     1051                            } 
     1052                            else { 
     1053                                Console 
     1054                                    .traceln(Level.WARNING, 
     1055                                             "End index of a Match before start. This should never happen"); 
     1056                            } 
     1057                        } 
     1058                    } 
     1059                    synchronized (appData) { 
     1060                        appData.detectedAndReplacedTasks = true; 
     1061                    } 
     1062                    
     1063 
     1064                    System.out.println("Replacing in Sequence " + oc.getSequenceId() + 
     1065                        " at position " + oc.getStartindex() + " till " + oc.getEndindex()); 
     1066                    final ISequenceInstance sequenceInstances = 
     1067                        RuleUtils.createNewSubSequenceInRange(appData.getSessions() 
     1068                                                                  .get(oc.getSequenceId()), oc 
     1069                                                                  .getStartindex(), oc 
     1070                                                                  .getEndindex(), (ISequence) appData.getPreparedTasks().get(pr.getMatchId()), 
     1071                                                              taskFactory, taskBuilder); 
     1072 
     1073                    // Adjust the length of the match regarding to the 
     1074                    // length of 
     1075                    // instance. (OptionalInstances may be shorter) 
     1076                    oc.setEndindex((oc.getStartindex() + sequenceInstances.size()) - 
     1077                        RuleUtils.missedOptionals); 
     1078 
     1079                    replacedOccurrences.add(oc); 
     1080 
     1081                } 
     1082            } 
     1083        } 
     1084    } 
     1085     
     1086    private static class PlannedReplacement { 
     1087        private int matchId; 
     1088        private int getMatchId() { 
     1089            return matchId; 
     1090        } 
     1091 
     1092        private MatchOccurrence getOccurrence() { 
     1093            return occurrence; 
     1094        } 
     1095 
     1096        private MatchOccurrence occurrence; 
     1097         
     1098        private PlannedReplacement(int matchId,MatchOccurrence occurrence) { 
     1099            this.matchId = matchId; 
     1100            this.occurrence = occurrence; 
     1101        } 
     1102    } 
     1103 
     1104    /** 
     1105     * The Class RuleApplicationData. 
     1106     */ 
     1107    private static class RuleApplicationData implements Serializable { 
     1108 
     1109        /** The Constant serialVersionUID. */ 
     1110        private static final long serialVersionUID = -7559657686755522960L; 
     1111 
     1112        /** 
     1113         * The number2task HashMap. Since we align the tasks just by their integer id, we need this 
     1114         * to convert them back to Tasks again 
     1115         */ 
     1116        private final HashMap<Integer, ITask> number2task; 
     1117 
     1118        /** 
     1119         * The unique tasks, keeps track about all unique tasks TODO: We Actually just need 
     1120         * number2task here, this structure can be removed in the future. 
     1121         */ 
     1122        private final HashSet<ITask> uniqueTasks; 
     1123 
     1124        /** 
     1125         * The substitution matrix used by the alignment algorithm to be able to compare distances 
     1126         * of tasks 
     1127         */ 
     1128        private final ObjectDistanceSubstitionMatrix submat; 
     1129 
     1130        private LinkedList<PlannedReplacement>[] plannedReplacements; 
     1131 
     1132        /** The list of all found matches */ 
     1133        private LinkedList<Match> matchseqs; 
     1134         
     1135        private ArrayList<ITask> preparedTasks; 
     1136 
     1137        public ArrayList<ITask> getPreparedTasks() { 
     1138            return preparedTasks; 
     1139        } 
     1140 
     1141        /** 
     1142         * The generated NumberSequences. This is the integer representation of the user sessions 
     1143         */ 
     1144        private ArrayList<NumberSequence> numberseqs; 
     1145 
     1146        /** The list of newly created tasks (of one iteration). */ 
     1147        private final LinkedList<ITask> newTasks; 
     1148 
     1149        /** The user sessions containing all EventTasks/Instances */ 
     1150        private final List<IUserSession> sessions; 
     1151 
     1152        /** True if we replaced something in the user sessions in one iteration. */ 
     1153        private boolean detectedAndReplacedTasks; 
     1154 
     1155        /** The result we return from this rule */ 
     1156        private final RuleApplicationResult result; 
     1157 
     1158        /** Stop Watch to measure performance */ 
     1159        private final StopWatch stopWatch; 
     1160 
     1161        /** 
     1162         * Instantiates a new rule application data. 
     1163         * 
     1164         * @param sessions 
     1165         *            The user sessions 
     1166         */ 
     1167        private RuleApplicationData(List<IUserSession> sessions) { 
     1168            this.sessions = sessions; 
     1169            numberseqs = new ArrayList<NumberSequence>(); 
     1170            uniqueTasks = new HashSet<ITask>(); 
     1171            number2task = new HashMap<Integer, ITask>(); 
     1172            stopWatch = new StopWatch(); 
     1173            result = new RuleApplicationResult(); 
     1174            preparedTasks = new ArrayList<ITask>(); 
     1175            submat = new ObjectDistanceSubstitionMatrix(maxSubstitutionScore, gapPenalty, false); 
     1176            newTasks = new LinkedList<ITask>(); 
     1177            this.detectedAndReplacedTasks = true; 
     1178        } 
     1179 
     1180        private void initializeQueues(int size) { 
     1181            plannedReplacements = new LinkedList[size]; 
     1182            for (int i = 0; i < size; i++) { 
     1183                plannedReplacements[i] = new LinkedList<PlannedReplacement>(); 
     1184            } 
     1185        } 
     1186 
     1187        public Queue<PlannedReplacement>[] getPlannedReplacements() { 
     1188            return plannedReplacements; 
     1189        } 
     1190 
     1191        /** 
     1192         * Detected and replaced tasks. 
     1193         * 
     1194         * @return true, if successful 
     1195         */ 
     1196        private boolean detectedAndReplacedTasks() { 
     1197            return detectedAndReplacedTasks; 
     1198        } 
     1199 
     1200        /** 
     1201         * Gets the match sequences. 
     1202         * 
     1203         * @return the match sequences 
     1204         */ 
     1205        public LinkedList<Match> getMatchseqs() { 
     1206            return matchseqs; 
     1207        } 
     1208 
     1209        /** 
     1210         * Gets the new tasks. 
     1211         * 
     1212         * @return the new tasks 
     1213         */ 
     1214        public LinkedList<ITask> getNewTasks() { 
     1215            return newTasks; 
     1216        } 
     1217 
     1218        /** 
     1219         * Gets the number2task. 
     1220         * 
     1221         * @return the number2task HashMap 
     1222         */ 
     1223        private HashMap<Integer, ITask> getNumber2Task() { 
     1224            return number2task; 
     1225        } 
     1226 
     1227        /** 
     1228         * Gets the number sequences. 
     1229         * 
     1230         * @return the number sequences 
     1231         */ 
     1232        private ArrayList<NumberSequence> getNumberSequences() { 
     1233            return numberseqs; 
     1234        } 
     1235 
     1236        /** 
     1237         * Gets the result. 
     1238         * 
     1239         * @return the result 
     1240         */ 
     1241        private RuleApplicationResult getResult() { 
     1242            return result; 
     1243        } 
     1244 
     1245        /** 
     1246         * Gets the sessions. 
     1247         * 
     1248         * @return the UserSessions as List. 
     1249         */ 
     1250        private List<IUserSession> getSessions() { 
     1251            return sessions; 
     1252        } 
     1253 
     1254        /** 
     1255         * Gets the stop watch. 
     1256         * 
     1257         * @return the stopWatch 
     1258         */ 
     1259        private StopWatch getStopWatch() { 
     1260            return stopWatch; 
     1261        } 
     1262 
     1263        /** 
     1264         * Gets the substitution matrix. 
     1265         * 
     1266         * @return the substitution matrix 
     1267         */ 
     1268        private ObjectDistanceSubstitionMatrix getSubmat() { 
     1269            return submat; 
     1270        } 
     1271 
     1272        /** 
     1273         * Gets the unique tasks. 
     1274         * 
     1275         * @return the unique tasks 
     1276         */ 
     1277        private HashSet<ITask> getUniqueTasks() { 
     1278            return uniqueTasks; 
     1279        } 
     1280 
     1281        /** 
     1282         * New task created. 
     1283         * 
     1284         * @param task 
     1285         *            can be called when new tasks are created to keep track of all newly created 
     1286         *            tasks 
     1287         */ 
     1288        private void newTaskCreated(ITask task) { 
     1289            number2task.put(task.getId(), task); 
     1290            newTasks.add(task); 
     1291        } 
     1292 
     1293        /** 
     1294         * Reset newly created tasks. 
     1295         */ 
     1296        synchronized private void resetNewlyCreatedTasks() { 
     1297            uniqueTasks.addAll(newTasks); 
     1298            newTasks.clear(); 
     1299        } 
     1300 
     1301        /** 
     1302         * Sets the number sequences. 
     1303         * 
     1304         * @param numberseqs 
     1305         *            the new number sequences 
     1306         */ 
     1307        private void setNumberSequences(ArrayList<NumberSequence> numberseqs) { 
     1308            this.numberseqs = numberseqs; 
     1309        } 
     1310 
     1311        /** 
     1312         * Update substitution matrix. 
     1313         */ 
     1314        private void updateSubstitutionMatrix() { 
     1315            submat.update(getNewTasks()); 
     1316            resetNewlyCreatedTasks(); 
     1317        } 
     1318 
     1319    } 
    13091320 
    13101321} 
Note: See TracChangeset for help on using the changeset viewer.