// Copyright 2012 Georg-August-Universität Göttingen, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package de.ugoe.cs.autoquest.tasktrees.temporalrelation; import java.util.Stack; import de.ugoe.cs.autoquest.eventcore.gui.IInteraction; import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask; import de.ugoe.cs.autoquest.tasktrees.treeifc.ISequence; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeBuilder; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNode; import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskTreeNodeFactory; /** * This rule generates sequences of events depending on the event types, more concrete, the * {@link IInteraction}s and the return values of their {@link IInteraction#startsLogicalSequence()} * and {@link IInteraction#finishesLogicalSequence()}. If a new logical sequence is started by * an interaction, then a real sequence is instantiated. The sequence is finished, if an * interaction finishes a logical sequence. Examples include keyboard focus changes. * * @version $Revision: $ $Date: 18.03.2012$ * @author 2012, last modified by $Author: patrick$ */ class DefaultGuiEventSequenceDetectionRule implements TemporalRelationshipRule { /** *

* the task tree node factory to be used for creating substructures for the temporal * relationships identified during rule *

*/ private ITaskTreeNodeFactory taskTreeNodeFactory; /** *

* the task tree builder to be used for creating substructures for the temporal relationships * identified during rule application *

*/ private ITaskTreeBuilder taskTreeBuilder; /** *

* instantiates the rule with a task tree node factory and builder to be used during rule * application. *

* * @param taskTreeNodeFactory the task tree node factory to be used for creating substructures * for the temporal relationships identified during rule * application * @param taskTreeBuilder the task tree builder to be used for creating substructures for * the temporal relationships identified during rule application */ DefaultGuiEventSequenceDetectionRule(ITaskTreeNodeFactory taskTreeNodeFactory, ITaskTreeBuilder taskTreeBuilder) { this.taskTreeNodeFactory = taskTreeNodeFactory; this.taskTreeBuilder = taskTreeBuilder; } /* * (non-Javadoc) * * @see de.ugoe.cs.tasktree.temporalrelation.TemporalRelationshipRule#apply(TaskTreeNode, * boolean) */ @Override public RuleApplicationResult apply(ITaskTreeNode parent, boolean finalize) { if (!(parent instanceof ISequence)) { return null; } RuleApplicationResult result = new RuleApplicationResult(); Stack sequenceStartingIndex = new Stack(); int index = 0; while (index < parent.getChildren().size()) { ITaskTreeNode child = parent.getChildren().get(index); if ((child instanceof IEventTask) && (((IEventTask) child).getEventType() instanceof IInteraction)) { IInteraction eventType = (IInteraction) ((IEventTask) child).getEventType(); if (eventType.finishesLogicalSequence() && (sequenceStartingIndex.size() > 0)) { index = handleLogicalSequence(sequenceStartingIndex, index, parent, result); } if (eventType.startsLogicalSequence()) { sequenceStartingIndex.push(index); } } index++; } if (sequenceStartingIndex.size() > 0) { if (!finalize) { result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FEASIBLE); } else { ITaskTreeNode lastChild = parent.getChildren().get(parent.getChildren().size() - 1); if (lastChild instanceof IEventTask) { handleLogicalSequence (sequenceStartingIndex, parent.getChildren().size() - 1, parent, result); } } } return result; } /** *

* TODO: comment *

* */ private int handleLogicalSequence(Stack sequenceStartingIndex, int index, ITaskTreeNode parent, RuleApplicationResult result) { int newIndex = index; IInteraction eventType = (IInteraction) ((IEventTask) parent.getChildren().get(index)).getEventType(); // There are several situations in which this implementation may cause infinite // loops. This is because the rule manager will reapply rules until // no rule is applied anymore. A sequence identified in a first iteration will // be identified as a sequence also in a second iteration. As an example // many sequences start with an interaction starting that sequence and end // with an interaction ending that sequence. This will be reidentified as // further subsequence. It must therefore be assured, that a sequence, that // was once identified is not reidentified in a further application of the rule. // For this, the implementation performs a kind of dry run. It creates a list of // children that would belong to an identified sequence. Only if this list is // not a reidentification then a new sequence is created and added to the // parent. If it is a reidentification can be identified, if the list of // children will contain all children of the parent, or if the list of children // only consists of one sequence. Further, an identified sequence must at least // have one child. boolean allChildrenBelongToSubSequence = (sequenceStartingIndex.peek() == 0) && (index == (parent.getChildren().size() - 1)); boolean atLeastOneChildToCondense = index - sequenceStartingIndex.peek() > 0; if (!allChildrenBelongToSubSequence && atLeastOneChildToCondense) { int startIndex = sequenceStartingIndex.pop(); ISequence sequence = taskTreeNodeFactory.createNewSequence(); for (int j = startIndex; j < index; j++) { taskTreeBuilder.addChild(sequence, parent.getChildren().get(startIndex)); taskTreeBuilder.removeChild((ISequence) parent, startIndex); } if (!eventType.startsLogicalSequence()) { taskTreeBuilder.addChild(sequence, parent.getChildren().get(startIndex)); taskTreeBuilder.removeChild((ISequence) parent, startIndex); newIndex = startIndex; } else { newIndex = startIndex + 1; } taskTreeBuilder.addChild((ISequence) parent, startIndex, sequence); result.addNewlyCreatedParentNode(sequence); taskTreeBuilder.setDescription(sequence, "logical sequence started by the first event"); result.setRuleApplicationStatus(RuleApplicationStatus.RULE_APPLICATION_FINISHED); } return newIndex; } }