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

Last change on this file since 970 was 970, checked in by pharms, 12 years ago
  • correction in merging selections of task tree nodes
  • Property svn:executable set to *
File size: 32.4 KB
Line 
1package de.ugoe.cs.autoquest.tasktrees.temporalrelation;
2
3import java.util.ArrayList;
4import java.util.List;
5
6import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEquality;
7import de.ugoe.cs.autoquest.tasktrees.nodeequality.NodeEqualityRuleManager;
8import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
9import de.ugoe.cs.autoquest.tasktrees.treeifc.IIteration;
10import de.ugoe.cs.autoquest.tasktrees.treeifc.ISelection;
11import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence;
12import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder;
13import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode;
14import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory;
15
16/**
17 * <p>
18 * iterations in a list of nodes are equal subsequences following each other directly. The
19 * subsequences can be of any length depending on the type of equality they need to have. If the
20 * subsequences have to be lexically equal, then they have to have the same length if they only
21 * contain event tasks. As an example entering text can be done through appropriate keystrokes or
22 * through pasting the text. As a result, two syntactically different sequences are semantically
23 * equal. If both follow each other, then they are an iteration of semantically equal children.
24 * But they are not lexically equal.
25 * </p>
26 * <p>
27 * This class determines equal subsequences following each other. It is provided with a minimal node
28 * equality the equal nodes should have. Through this, it is possible to find e.g. lexically
29 * equal subsequence through a first application of this rule and semantically equal children to
30 * a later application of this rule. This is used by the {@link TemporalRelationshipRuleManager}
31 * which instantiates this rule three times, each with a different minimal equality.
32 * </p>
33 * <p>
34 * The equal subsequences are determined through trial and error. This algorithm has a high effort
35 * as it tries in the worst case all possible combinations of sub lists in all possible parts of
36 * the list of children of a provided parent node. The steps for each trial are.
37 * <ul>
38 *   <li>for all possible subparts of the children of the provided parent
39 *   <ul>
40 *     <li>for all possible first sublists in the subpart
41 *     <ul>
42 *       <li>for all succeeding next sublists in this part</li>
43 *       <ul>
44 *         <li>check if this sublist is equal to all previously identified sublist in this part</li>
45 *       </ul>
46 *     </ul>
47 *     <li>
48 *       if a combination of sublists is found in this subpart which are all equal to each other
49 *       at the provided minimal equality level, an iteration in this subpart was found.
50 *     </li>
51 *       <ul>
52 *         <li>merge the identified equal sublists to an iteration</li>
53 *       </ul>
54 *   </ul>
55 * </ul>
56 * The algorithm tries to optimize if all children are event tasks and if the sublists shall be
57 * lexically equal. In this case, the sublist all have to have the same length. The trial and
58 * error reduces to a minimum of possible sublists.
59 * </p>
60 *
61 * @author Patrick Harms
62 */
63public class DefaultIterationDetectionRule implements TemporalRelationshipRule {
64   
65    /**
66     * <p>
67     * the node equality manager needed for comparing task tree nodes with each other
68     * </p>
69     */
70    private NodeEqualityRuleManager nodeEqualityRuleManager;
71
72    /**
73     * <p>
74     * the minimal node equality two identified sublists need to have to consider them as equal
75     * and to create an iteration for
76     * </p>
77     */
78    private NodeEquality minimalNodeEquality;
79
80    /**
81     * <p>
82     * instantiates the rule and initializes it with a node equality rule manager and the minimal
83     * node equality identified sublist must have to consider them as iterated.
84     * </p>
85     */
86    DefaultIterationDetectionRule(NodeEqualityRuleManager nodeEqualityRuleManager,
87                                  NodeEquality            minimalNodeEquality)
88    {
89        super();
90        this.nodeEqualityRuleManager = nodeEqualityRuleManager;
91        this.minimalNodeEquality = minimalNodeEquality;
92    }
93
94    /*
95     * (non-Javadoc)
96     *
97     * @see TemporalRelationshipRule#apply(TaskTreeNode, TaskTreeBuilder, TaskTreeNodeFactory)
98     */
99    @Override
100    public RuleApplicationResult apply(ITaskTreeNode        parent,
101                                       ITaskTreeBuilder     treeBuilder,
102                                       ITaskTreeNodeFactory nodeFactory,
103                                       boolean              finalize)
104    {
105        if (!(parent instanceof ISequence)) {
106            return null;
107        }
108
109        if (!finalize) {
110            // the rule is always feasible as iterations may occur at any time
111            RuleApplicationResult result = new RuleApplicationResult();
112            result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FEASIBLE);
113            return result;
114        }
115
116        // parent must already have at least 2 children
117        if ((parent.getChildren() == null) || (parent.getChildren().size() < 2)) {
118            return null;
119        }
120       
121       
122        SubSequences subSequences = getEqualSubsequences(parent, treeBuilder, nodeFactory);
123
124        if (subSequences != null) {
125            RuleApplicationResult result = new RuleApplicationResult();
126
127            mergeEqualNodes(subSequences.equalVariants, treeBuilder, nodeFactory);
128            IIteration newIteration = createIterationBasedOnIdentifiedVariants
129                (subSequences, treeBuilder, nodeFactory, result);
130
131            determineNewlyCreatedParentTasks(parent, newIteration, result);
132           
133            // remove iterated children
134            for (int j = subSequences.start; j < subSequences.end; j++) {
135                treeBuilder.removeChild((ISequence) parent, subSequences.start);
136            }
137
138            // add the new iteration instead
139            treeBuilder.addChild((ISequence) parent, subSequences.start, newIteration);
140
141            result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FINISHED);
142            return result;
143        }
144
145        return null;
146    }
147
148    /**
149     * <p>
150     * this method initiates the trial and error algorithm denoted in the description of this class.
151     * Its main purpose is the selection of a subpart of all children in the parent node in which
152     * equal sublists shall be searched. It is important, to always find the last iterations in a
153     * part first. The reason for this are iterations of iterations. If we always found the first
154     * iteration in a subpart first, then this may be an iteration of iterations. However, there
155     * may be subsequent iterations to be included in this iteration. But these iterations are not
156     * found yet, as they occur later in the sequence. Therefore, if we always find the last
157     * iteration in a sequence first, iterations of iterations are identified, last.
158     * </p>
159     *
160     * @param parent      the parent node in which iterations of children shall be found
161     * @param treeBuilder the tree builder that can be used for connecting task tree nodes
162     * @param nodeFactory the node factory that can be used for instantiating task tree nodes
163     *
164     * @return the iterated subsequences identified in a specific part (contains the equal
165     *         subsequences as well as the start (inclusive) and end (exclusive) index of the
166     *         subpart in which the sequences were found)
167     */
168    private SubSequences getEqualSubsequences(ITaskTreeNode        parent,
169                                              ITaskTreeBuilder     treeBuilder,
170                                              ITaskTreeNodeFactory nodeFactory)
171    {
172        SubSequences subSequences = null;
173
174        // to find longer iterations first, start with long sequences
175        FIND_ITERATION:
176        for (int end = parent.getChildren().size(); end > 0; end--) {
177            for (int start = 0; start < end; start++) {
178                boolean useEqualSublistLengths = equalSublistLengthsCanBeUsed(parent, start, end);
179
180                subSequences = new SubSequences();
181                subSequences.start = start;
182
183                boolean foundFurtherVariants = findFurtherVariants
184                    (subSequences, parent, start, end, treeBuilder, nodeFactory,
185                     useEqualSublistLengths);
186
187                if (foundFurtherVariants) {
188                    break FIND_ITERATION;
189                }
190                else {
191                    subSequences = null;
192                }
193            }
194        }
195       
196        return subSequences;
197    }
198
199    /**
200     * <p>
201     * for optimization purposes, we check if the length of the sublists to be identified as
202     * iterations has to be the same for any sublist. This only applies, if the minimum node
203     * equality to be checked for is lexical equality. If the children of the parent are all event
204     * tasks, then sublists can only be lexically equal, if they all have the same length.
205     * Therefore we check, if the minimal node equality is lexical equality. And if so, we also
206     * check if all children of the parent in which an iteration shall be searched for are event
207     * tasks.
208     * </p>
209     *
210     * @param parent the parent node to search for iterations of its children
211     * @param start  the beginning of the subpart (inclusive) to be considered
212     * @param end    the end of the subpart (exclusive) to be considered
213     *
214     * @return true, if the sublists must have the same lengths, false else
215     */
216    private boolean equalSublistLengthsCanBeUsed(ITaskTreeNode parent, int start, int end) {
217        boolean equalLengthsCanBeUsed = minimalNodeEquality.isAtLeast(NodeEquality.LEXICALLY_EQUAL);
218       
219        if (equalLengthsCanBeUsed) {
220            for (int i = start; i < end; i++) {
221                if (!(parent.getChildren().get(i) instanceof IEventTask)) {
222                    equalLengthsCanBeUsed = false;
223                    break;
224                }
225            }
226        }
227
228        return equalLengthsCanBeUsed;
229    }
230
231    /**
232     * <p>
233     * this method starts at a specific position in the list of children of the provided parent
234     * and checks, if it finds a further sublist, that matches the already found sublists. If
235     * the sublist lengths must be equal, it only searches for a sublist of the same length of the
236     * already found sublists. The method calls itself if it identifies a further equal sublist but
237     * if the end of the subpart of children is not yet reached.
238     * </p>
239     *
240     * @param subSequences           the sublist found so far against which equality of the next
241     *                               sublist must be checked
242     * @param parent                 the parent node of which the children are analyzed
243     * @param start                  the starting index from which to start the next sublist to be
244     *                               identified
245     * @param end                    the end index (exclusive) of the current subpart of children
246     *                               in which iterations are searched for
247     * @param treeBuilder            the tree builder that can be used for connecting task tree
248     *                               nodes
249     * @param nodeFactory            the node factory that can be used for instantiating task tree
250     *                               nodes
251     * @param useEqualSublistLengths true if the sublists to be searched for all need to have the
252     *                               same length
253     *
254     * @return true if a further equal variant was found, false else
255     */
256    private boolean findFurtherVariants(SubSequences         subSequences,
257                                        ITaskTreeNode        parent,
258                                        int                  start,
259                                        int                  end,
260                                        ITaskTreeBuilder     treeBuilder,
261                                        ITaskTreeNodeFactory nodeFactory,
262                                        boolean              useEqualSublistLengths)
263    {
264        boolean foundFurtherVariants = (start == end) && (subSequences.equalVariants.size() > 1);
265       
266        int minChildCount = 1;
267        int maxChildCount = end - start;
268       
269        if (useEqualSublistLengths && (subSequences.equalVariants.size() > 0)) {
270            minChildCount = subSequences.equalVariants.get(0).getChildren().size();
271            maxChildCount = Math.min(minChildCount, maxChildCount);
272        }
273       
274        for (int childCount = minChildCount; childCount <= maxChildCount; childCount++) {
275            if (useEqualSublistLengths && (((end - start) % childCount) != 0)) {
276                continue;
277            }
278           
279            ISequence furtherVariant = nodeFactory.createNewSequence();
280           
281            for (int j = start; j < start + childCount; j++) {
282                treeBuilder.addChild(furtherVariant, parent.getChildren().get(j));
283            }
284           
285            boolean allVariantsEqual = true;
286           
287            for (ITaskTreeNode equalVariant : subSequences.equalVariants) {
288                NodeEquality nodeEquality =
289                    nodeEqualityRuleManager.applyRules(equalVariant, furtherVariant);
290               
291                if (!nodeEquality.isAtLeast(minimalNodeEquality)) {
292                    allVariantsEqual = false;
293                    break;
294                }
295            }
296           
297            if (allVariantsEqual) {
298               
299                // we found a further variant. Add it to the list of variants and try to find
300                // further variants. Ignore, if none is available
301                int index = subSequences.equalVariants.size();
302                subSequences.equalVariants.add(index, furtherVariant);
303               
304                foundFurtherVariants = findFurtherVariants
305                    (subSequences, parent, start + childCount, end, treeBuilder, nodeFactory,
306                     useEqualSublistLengths);
307
308                if (foundFurtherVariants) {
309                    subSequences.end = end;
310                    break;
311                }
312                else {
313                    subSequences.equalVariants.remove(index);
314                }
315            }
316        }
317       
318        return foundFurtherVariants;
319    }
320
321    /**
322     * <p>
323     * this method merges task tree nodes in a list, if they can be merged. for this, it tries
324     * to merge every node with every other node in the provided list using the
325     * {@link #mergeEqualTasks(ITaskTreeNode, ITaskTreeNode, ITaskTreeBuilder, ITaskTreeNodeFactory)}
326     * method. If a merge is possible, it removes the merged nodes from the list and adds the
327     * merge result.
328     * </p>
329     *
330     * @param nodes       the list of nodes to be merged
331     * @param treeBuilder the tree builder that can be used for connecting task tree nodes
332     * @param nodeFactory the node factory that can be used for instantiating task tree nodes
333     */
334    private void mergeEqualNodes(List<ITaskTreeNode>   nodes,
335                                 ITaskTreeBuilder      treeBuilder,
336                                 ITaskTreeNodeFactory  nodeFactory)
337    {
338        int index1 = 0;
339        int index2 = 0;
340        ITaskTreeNode variant1;
341        ITaskTreeNode variant2;
342       
343        while (index1 < nodes.size()) {
344            variant1 = nodes.get(index1);
345            index2 = index1 + 1;
346           
347            while (index2 < nodes.size()) {
348                variant2 = nodes.get(index2);
349                ITaskTreeNode mergedChild =
350                    mergeEqualTasks(variant1, variant2, treeBuilder, nodeFactory);
351               
352                if (mergedChild != null) {
353                    // if we merged something start from the beginning to perform the next merge
354                    nodes.remove(index2);
355                    nodes.remove(index1);
356                    nodes.add(index1, mergedChild);
357                    index1 = -1;
358                    break;
359                }
360                else {
361                    index2++;
362                }
363            }
364           
365            index1++;
366        }
367    }
368
369    /**
370     * <p>
371     * this method merges two equal tasks with each other if possible. If the tasks are lexically
372     * equal, the first of them is returned as merge result. If both tasks are of the same
373     * temporal relationship type, the appropriate merge method is called to merge them. If one
374     * of the nodes is a selection, the other one is added as a variant of this selection.
375     * (However, if both nodes are selections, they are merged using the appropriate merge method.)
376     * If merging is not possible, then a selection of both provided nodes is created and
377     * returned as merge result.
378     * </p>
379     *
380     * @param node1       the first task to be merged
381     * @param node2       the second task to be merged
382     * @param treeBuilder the tree builder that can be used for connecting task tree nodes
383     * @param nodeFactory the node factory that can be used for instantiating task tree nodes
384     *
385     * @return the result of the merge
386     */
387    private ITaskTreeNode mergeEqualTasks(ITaskTreeNode         node1,
388                                          ITaskTreeNode         node2,
389                                          ITaskTreeBuilder      treeBuilder,
390                                          ITaskTreeNodeFactory  nodeFactory)
391    {
392        ITaskTreeNode mergeResult = null;
393       
394        if ((node1 instanceof ISequence) && (node2 instanceof ISequence)) {
395            mergeResult = mergeEqualSequences
396                ((ISequence) node1, (ISequence) node2, treeBuilder, nodeFactory);
397        }
398        else if ((node1 instanceof ISelection) && (node2 instanceof ISelection)) {
399            mergeResult = mergeEqualSelections
400                ((ISelection) node1, (ISelection) node2, treeBuilder, nodeFactory);
401        }
402        else if ((node1 instanceof IIteration) && (node2 instanceof IIteration)) {
403            mergeResult = mergeEqualIterations
404                ((IIteration) node1, (IIteration) node2, treeBuilder, nodeFactory);
405        }
406        else if (node1 instanceof ISelection) {
407            treeBuilder.addChild((ISelection) node1, node2);
408            mergeResult = node1;
409        }
410        else if (node2 instanceof ISelection) {
411            treeBuilder.addChild((ISelection) node2, node1);
412            mergeResult = node2;
413        }
414        else if (node1 instanceof IIteration) {
415            mergeResult = mergeEqualTasks
416                (((IIteration) node1).getChildren().get(0), node2, treeBuilder, nodeFactory);
417           
418            if (mergeResult != null) {
419                IIteration iteration = nodeFactory.createNewIteration();
420                treeBuilder.setChild(iteration, mergeResult);
421                mergeResult = iteration;
422            }
423        }
424        else if (node2 instanceof IIteration) {
425            mergeResult = mergeEqualTasks
426                (((IIteration) node2).getChildren().get(0), node1, treeBuilder, nodeFactory);
427           
428            if (mergeResult != null) {
429                IIteration iteration = nodeFactory.createNewIteration();
430                treeBuilder.setChild(iteration, mergeResult);
431                mergeResult = iteration;
432            }
433        }
434        else {
435            NodeEquality nodeEquality = nodeEqualityRuleManager.applyRules(node1, node2);
436           
437            if (nodeEquality.isAtLeast(NodeEquality.LEXICALLY_EQUAL)) {
438                mergeResult = node1;
439            }
440        }
441
442        if (mergeResult == null) {
443            mergeResult = nodeFactory.createNewSelection();
444            treeBuilder.addChild((ISelection) mergeResult, node1);
445            treeBuilder.addChild((ISelection) mergeResult, node2);
446        }
447       
448        return mergeResult;
449    }
450
451    /**
452     * <p>
453     * merges equal sequences. This is done through trying to merge each node of sequence 1 with
454     * the node in sequence 2 being located at the same position. If not all children can be merged
455     * or if the sequences have different lengths, null is returned to indicate, that merging is
456     * not possible. For merging children, the
457     * {@link #mergeEqualTasks(ITaskTreeNode, ITaskTreeNode, ITaskTreeBuilder, ITaskTreeNodeFactory)}
458     * method is called.
459     * </p>
460     *
461     * @param sequence1   the first sequence to be merged
462     * @param sequence2   the second sequence to be merged
463     * @param treeBuilder the tree builder that can be used for connecting task tree nodes
464     * @param nodeFactory the node factory that can be used for instantiating task tree nodes
465     *
466     * @return the result of the merge or null if merging was not possible
467     */
468    private ISequence mergeEqualSequences(ISequence             sequence1,
469                                          ISequence             sequence2,
470                                          ITaskTreeBuilder      treeBuilder,
471                                          ITaskTreeNodeFactory  nodeFactory)
472    {
473        ISequence mergeResult = null;
474       
475        if (sequence1.getChildren().size() == sequence2.getChildren().size()) {
476            mergeResult = nodeFactory.createNewSequence();
477           
478            for (int i = 0; i < sequence1.getChildren().size(); i++) {
479                ITaskTreeNode mergedNode = mergeEqualTasks
480                    (sequence1.getChildren().get(i), sequence2.getChildren().get(i),
481                     treeBuilder, nodeFactory);
482               
483                if (mergedNode != null) {
484                    treeBuilder.addChild(mergeResult, mergedNode);
485                }
486                else {
487                    mergeResult = null;
488                    break;
489                }
490            }
491        }
492       
493        return mergeResult;
494    }
495
496    /**
497     * <p>
498     * merges equal selections. This is done by adding those children of the second selection to
499     * the first selection that can not be merged with any of the children of the first selection.
500     * If a merge is possible and this merge is not a simple selection of the merged children,
501     * then the merged child replaces the child of the first selection which was merged.
502     * </p>
503     *
504     * @param selection1  the first selection to be merged
505     * @param selection2  the second selection to be merged
506     * @param treeBuilder the tree builder that can be used for connecting task tree nodes
507     * @param nodeFactory the node factory that can be used for instantiating task tree nodes
508     *
509     * @return the result of the merge which is never null
510     */
511    private ITaskTreeNode mergeEqualSelections(ISelection            selection1,
512                                               ISelection            selection2,
513                                               ITaskTreeBuilder      treeBuilder,
514                                               ITaskTreeNodeFactory  nodeFactory)
515    {
516        ISelection mergeResult = selection1;
517       
518        ITaskTreeNode childToMerge = null;
519        ITaskTreeNode mergedChild = null;
520       
521        // check for each child of selection 2 if it is a duplicate of one of the children
522        // if selection 1. If not, add it as further child to the merge result, else skip it.
523        for (int i = 0; i < selection2.getChildren().size(); i++) {
524            childToMerge = selection2.getChildren().get(i);
525            for (int j = 0; j < selection1.getChildren().size(); j++) {
526                mergedChild = mergeEqualTasks
527                    (selection1.getChildren().get(j), childToMerge, treeBuilder, nodeFactory);
528               
529                // a merge must not be a selection, except it is one of the children. Otherwise
530                // no real merge was done.
531                if ((mergedChild != null) &&
532                    ((!(mergedChild instanceof ISelection)) ||
533                     (selection1.getChildren().get(j) == mergedChild) ||
534                     (childToMerge == mergedChild)))
535                {
536                    // we found a real merge. So replace the original child in selection 1 with
537                    // the merged child
538                    treeBuilder.removeChild(selection1, selection1.getChildren().get(j));
539                    treeBuilder.addChild(selection1, mergedChild);
540                    mergedChild = null;
541                    childToMerge = null;
542                    break;
543                }
544            }
545           
546            if (childToMerge != null) {
547                treeBuilder.addChild(selection1, childToMerge);
548            }
549        }
550       
551        return mergeResult;
552    }
553
554    /**
555     * <p>
556     * merges equal iterations. This is done through merging the children of both iterations. If
557     * this is possible, a resulting iteration with the merge result of the children as its own
558     * child is returned. Otherwise null is returned to indicate that merging was not possible.
559     * </p>
560     *
561     * @param selection1  the first iteration to be merged
562     * @param selection2  the second iteration to be merged
563     * @param treeBuilder the tree builder that can be used for connecting task tree nodes
564     * @param nodeFactory the node factory that can be used for instantiating task tree nodes
565     *
566     * @return the result of the merge or null if merging is not possible
567     */
568    private ITaskTreeNode mergeEqualIterations(IIteration            iteration1,
569                                               IIteration            iteration2,
570                                               ITaskTreeBuilder      treeBuilder,
571                                               ITaskTreeNodeFactory  nodeFactory)
572    {
573        ITaskTreeNode mergedChild = mergeEqualTasks
574            (iteration1.getChildren().get(0), iteration2.getChildren().get(0),
575             treeBuilder, nodeFactory);
576       
577        IIteration mergeResult = null;
578       
579        if (mergedChild != null) {
580            mergeResult = nodeFactory.createNewIteration();
581            treeBuilder.setChild(mergeResult, mergedChild);
582        }
583       
584        return mergeResult;
585    }
586
587    /**
588     * <p>
589     * this is a convenience method to create an iteration based on the identified and already
590     * merged iterated subsequences. This method creates the simplest iteration possible. As an
591     * example, if always the same task tree node is iterated, it becomes the child of the
592     * iteration. If a sequence of tasks is iterated, this sequence becomes the child of the
593     * iteration. It several equal sublists or nodes which are not lexically equal are iterated
594     * they become a selection which in turn become the child of the iteration.
595     * </p>
596     *
597     * @param subsequences the identified and already merged equal subsequences
598     * @param treeBuilder  the tree builder that can be used for connecting task tree nodes
599     * @param nodeFactory  the node factory that can be used for instantiating the iteration
600     *
601     * @return the resulting iteration
602     */
603    private IIteration createIterationBasedOnIdentifiedVariants(SubSequences          subsequences,
604                                                                ITaskTreeBuilder      treeBuilder,
605                                                                ITaskTreeNodeFactory  nodeFactory,
606                                                                RuleApplicationResult result)
607    {
608        IIteration newIteration = nodeFactory.createNewIteration();
609        result.addNewlyCreatedParentNode(newIteration);
610
611        if (subsequences.equalVariants.size() == 1) {
612            // all children are the same. Create an iteration of this child
613            if (subsequences.equalVariants.get(0).getChildren().size() == 1) {
614                // there is only one equal variant and this has only one child. So create an
615                // iteration of this child
616                treeBuilder.setChild
617                    (newIteration, subsequences.equalVariants.get(0).getChildren().get(0));
618            }
619            else {
620                // there was an iteration of one equal sequence
621                treeBuilder.setChild(newIteration, subsequences.equalVariants.get(0));
622                result.addNewlyCreatedParentNode(subsequences.equalVariants.get(0));
623            }
624        }
625        else {
626            // there are distinct variants of equal subsequences or children --> create an
627            // iterated selection
628            ISelection selection = nodeFactory.createNewSelection();
629            result.addNewlyCreatedParentNode(selection);
630
631            for (ITaskTreeNode variant : subsequences.equalVariants) {
632                if (variant.getChildren().size() == 1) {
633                    treeBuilder.addChild(selection, variant.getChildren().get(0));
634                }
635                else {
636                    treeBuilder.addChild(selection, variant);
637                    result.addNewlyCreatedParentNode(variant);
638                }
639            }
640
641            treeBuilder.setChild(newIteration, selection);
642        }
643       
644        return newIteration;
645    }
646
647    /**
648     * <p>
649     * as the method has to denote all newly created parent nodes this method identifies them by
650     * comparing the existing subtree with the newly created iteration. Only those parent nodes
651     * in the new iteration, which are not already found in the existing sub tree are denoted as
652     * newly created. We do this in this way, as during the iteration detection algorithm, many
653     * parent nodes are created, which may be discarded later. It is easier to identify the
654     * remaining newly created parent nodes through this way than to integrate it into the
655     * algorithm.
656     * </p>
657     *
658     * @param existingSubTree the existing subtree
659     * @param newSubTree      the identified iteration
660     * @param result          the rule application result into which the newly created parent nodes
661     *                        shall be stored.
662     */
663    private void determineNewlyCreatedParentTasks(ITaskTreeNode         existingSubTree,
664                                                  ITaskTreeNode         newSubTree,
665                                                  RuleApplicationResult result)
666    {
667        List<ITaskTreeNode> existingParentNodes = getParentNodes(existingSubTree);
668        List<ITaskTreeNode> newParentNodes = getParentNodes(newSubTree);
669       
670        boolean foundNode;
671        for (ITaskTreeNode newParentNode : newParentNodes) {
672            foundNode = false;
673            for (ITaskTreeNode existingParentNode : existingParentNodes) {
674                // It is sufficient to compare the references. The algorithm reuses nodes as they
675                // are. So any node existing in the new structure that is also in the old structure
676                // was unchanged an therefore does not need to be handled as a newly created one.
677                // but every node in the new structure that is not included in the old structure
678                // must be treated as a newly created one.
679                if (newParentNode == existingParentNode) {
680                    foundNode = true;
681                    break;
682                }
683            }
684           
685            if (!foundNode) {
686                result.addNewlyCreatedParentNode(newParentNode);
687            }
688        }
689       
690    }
691
692    /**
693     * <p>
694     * convenience method to determine all parent nodes existing in a subtree
695     * </p>
696     *
697     * @param subtree the subtree to search for parent nodes in
698     *
699     * @return a list of parent nodes existing in the subtree
700     */
701    private List<ITaskTreeNode> getParentNodes(ITaskTreeNode subtree) {
702        List<ITaskTreeNode> parentNodes = new ArrayList<ITaskTreeNode>();
703       
704        if (subtree.getChildren().size() > 0) {
705            parentNodes.add(subtree);
706           
707            for (ITaskTreeNode child : subtree.getChildren()) {
708                parentNodes.addAll(getParentNodes(child));
709            }
710        }
711       
712        return parentNodes;
713    }
714
715    /**
716     * <p>
717     * used to have a container for equal sublists identified in a sub part of the children of
718     * a parent node.
719     * </p>
720     *
721     * @author Patrick Harms
722     */
723    private static class SubSequences {
724
725        /**
726         * <p>
727         * the beginning of the subpart of the children of the parent node in which the sublists
728         * are found (inclusive)
729         * </p>
730         */
731        public int start;
732       
733        /**
734         * <p>
735         * the end of the subpart of the children of the parent node in which the sublists
736         * are found (exclusive)
737         * </p>
738         */
739        public int end;
740       
741        /**
742         * <p>
743         * the equal sublists found in the subpart of the children of the parent node
744         * </p>
745         */
746        List<ITaskTreeNode> equalVariants = new ArrayList<ITaskTreeNode>();
747       
748    }
749
750}
Note: See TracBrowser for help on using the repository browser.