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

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