// 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 java.lang.Object#toString()
*/
@Override
public String toString() {
return "DefaultGuiEventSequenceDetectionRule";
}
/*
* (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;
}
}