package de.ugoe.cs.autoquest.tasktrees.temporalrelation; import java.util.ArrayList; import java.util.List; import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement; 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; /** * TODO: comment or delete * * @version $Revision: $ $Date: 18.03.2012$ * @author 2012, last modified by $Author: patrick$ */ public class DefaultGuiElementSequenceDetectionRuleBad implements TemporalRelationshipRule { /** * */ private List> guiElementFilter; /** * */ DefaultGuiElementSequenceDetectionRuleBad() { this.guiElementFilter = null; } /** * */ DefaultGuiElementSequenceDetectionRuleBad(List> guiElementFilter) { this.guiElementFilter = guiElementFilter; } /* * (non-Javadoc) * * @see de.ugoe.cs.tasktree.temporalrelation.TemporalRelationshipRule#apply(TaskTreeNode, * TaskTreeBuilder, TaskTreeNodeFactory) */ @Override public RuleApplicationResult apply(ITaskTreeNode parent, ITaskTreeBuilder builder, ITaskTreeNodeFactory nodeFactory, boolean finalize) { if (!(parent instanceof ISequence)) { return null; } List children = parent.getChildren(); RuleApplicationResult result = new RuleApplicationResult(); IGUIElement lastGuiElement = null; int index = 0; while (index <= children.size()) { ITaskTreeNode child = index < children.size() ? children.get(index) : null; IGUIElement currentGuiElement = getGuiElement(child); if ((index > 0) && (!guiElementsEqual(lastGuiElement, currentGuiElement))) { ReducableCommonDenominator commonDenominator = getNextReducableCommonDenominator(parent, index - 1); RuleApplicationStatus status = handleCommonDenominator (commonDenominator, parent, index, lastGuiElement, currentGuiElement, builder, nodeFactory, finalize, result); if (status != null) { result.setRuleApplicationStatus(status); return result; } // else go on } lastGuiElement = currentGuiElement; index++; } return result; } /** * */ private ReducableCommonDenominator getNextReducableCommonDenominator(ITaskTreeNode parent, int childIndex) { ReducableCommonDenominator commonDenominator = null; // a common denominator can only exist for at least two task tree nodes if (childIndex > 0) { // start with the last one int pos = childIndex; commonDenominator = new ReducableCommonDenominator(); // check for further predecessors, if they match the same common denominator IGUIElement currentCommonDenominator = null; do { if (--pos < 0) { currentCommonDenominator = null; } else { currentCommonDenominator = getCommonDenominator (getGuiElement(parent.getChildren().get(pos)), getGuiElement(parent.getChildren().get(pos + 1))); } if (commonDenominator.commonGuiElement == null) { commonDenominator.commonGuiElement = currentCommonDenominator; } } while ((commonDenominator.commonGuiElement != null) && (commonDenominator.commonGuiElement.equals(currentCommonDenominator))); if (commonDenominator.commonGuiElement != null) { // pos points to the last element, that has not the same common denominator. // This one must be subtracted from the task number as well commonDenominator.noOfTasks = childIndex - pos; } else { commonDenominator = null; } } return commonDenominator; } /** * */ private RuleApplicationStatus handleCommonDenominator(ReducableCommonDenominator commonDenominator, ITaskTreeNode parent, int currentIndex, IGUIElement lastGuiElement, IGUIElement currentGuiElement, ITaskTreeBuilder builder, ITaskTreeNodeFactory nodeFactory, boolean finalize, RuleApplicationResult result) { List children = parent.getChildren(); boolean sequenceHasOnlyOneChild = children.size() == 1; boolean reachedEndOfSequence = currentIndex == children.size(); boolean haveCommonDenominator = commonDenominator != null; boolean allChildrenShareDenominator = haveCommonDenominator && commonDenominator.noOfTasks == children.size(); boolean nextChildSharesDenominator = haveCommonDenominator && !reachedEndOfSequence && isOnGuiElementPath(commonDenominator.commonGuiElement, currentGuiElement); IGUIElement denominatorOfPreviousAndCurrentChild = !reachedEndOfSequence ? getCommonDenominator(lastGuiElement, currentGuiElement) : null; boolean previousAndCurrentChildHaveDenominator = denominatorOfPreviousAndCurrentChild != null; if (haveCommonDenominator) { if (!reachedEndOfSequence) { if (nextChildSharesDenominator) { // the last child, although matching the identified common denominator, may // stand on its own because it is even deeper in the hierarchy, than the // common denominator as well as the common denominator with the next child. // So there need to appropriate subsequences to distinguish the child from // the hierarchy of the next one. if (isOnGuiElementPath(denominatorOfPreviousAndCurrentChild, lastGuiElement)) { return condenseChildToSequencesRepresentingHierarchy (parent, lastGuiElement, denominatorOfPreviousAndCurrentChild, currentIndex - 1, builder, nodeFactory, result); } else { // go on return null; } } else { condenseTasksToSequencesRepresentingHierarchy (commonDenominator.commonGuiElement, currentGuiElement, parent, currentIndex, commonDenominator.noOfTasks, builder, nodeFactory, result); return RuleApplicationStatus.RULE_APPLICATION_FINISHED; } } else { // end of sequence is reached and denominator is found if (!allChildrenShareDenominator) { condenseTasksToSequencesRepresentingHierarchy (commonDenominator.commonGuiElement, currentGuiElement, parent, currentIndex, commonDenominator.noOfTasks, builder, nodeFactory, result); return RuleApplicationStatus.RULE_APPLICATION_FINISHED; } else { // all children share denominator if (finalize) { if (ensureSequencesRepresentingHierarchy (parent, commonDenominator.commonGuiElement, builder, nodeFactory, result)) { return RuleApplicationStatus.RULE_APPLICATION_FINISHED; } else { return condenseChildToSequencesRepresentingHierarchy (parent, lastGuiElement, getGuiElement(parent), currentIndex - 1, builder, nodeFactory, result); } } else { return RuleApplicationStatus.RULE_APPLICATION_FEASIBLE; } } } } else { // no common denominator found if (!reachedEndOfSequence) { if (previousAndCurrentChildHaveDenominator) { // go on return null; } else { return condenseChildToSequencesRepresentingHierarchy (parent, lastGuiElement, getGuiElement(parent), currentIndex - 1, builder, nodeFactory, result); } } else { // last child has its own GUI hierarchy if (sequenceHasOnlyOneChild) { if (finalize) { if (ensureSequencesRepresentingHierarchy (parent, lastGuiElement, builder, nodeFactory, result)) { return RuleApplicationStatus.RULE_APPLICATION_FINISHED; } else { return RuleApplicationStatus.RULE_NOT_APPLIED; } } else { return RuleApplicationStatus.RULE_APPLICATION_FEASIBLE; } } else { if (finalize) { return condenseChildToSequencesRepresentingHierarchy (parent, lastGuiElement, getGuiElement(parent), currentIndex - 1, builder, nodeFactory, result); } else { return RuleApplicationStatus.RULE_APPLICATION_FEASIBLE; } } } } } /** * */ private IGUIElement getCommonDenominator(List guiElements) { IGUIElement commonDenominator = null; if (guiElements.size() > 0) { List commonDenominatorPath = new ArrayList(); // create a reference list using the first GUI element IGUIElement guiElement = guiElements.get(0); while (guiElement != null) { if (guiElementMatchesConsideredTypes(guiElement)) { commonDenominatorPath.add(0, guiElement); } guiElement = guiElement.getParent(); } if (commonDenominatorPath.size() == 0) { return null; } // for each other GUI element, check the reference list for the first element in the // path, that is not common to the current one, and delete it as well as it subsequent // siblings List currentPath = new ArrayList(); for (int i = 1; i < guiElements.size(); i++) { currentPath.clear(); guiElement = guiElements.get(i); while (guiElement != null) { if (guiElementMatchesConsideredTypes(guiElement)) { currentPath.add(0, guiElement); } guiElement = guiElement.getParent(); } // determine the index of the first unequal path element int index = 0; while ((index < commonDenominatorPath.size()) && (index < currentPath.size()) && commonDenominatorPath.get(index).equals(currentPath.get(index))) { index++; } // remove all elements from the common denonimator path, that do not match while (index < commonDenominatorPath.size()) { commonDenominatorPath.remove(index); } } if (commonDenominatorPath.size() > 0) { commonDenominator = commonDenominatorPath.get(commonDenominatorPath.size() - 1); } } return commonDenominator; } /** * */ private IGUIElement getCommonDenominator(IGUIElement guiElement1, IGUIElement guiElement2) { if ((guiElement1 == null) || (guiElement2 == null)) { return null; } List allGuiElements = new ArrayList(); allGuiElements.add(guiElement1); allGuiElements.add(guiElement2); return getCommonDenominator(allGuiElements); } /** * */ private void condenseTasksToSequencesRepresentingHierarchy(IGUIElement lastGuiElement, IGUIElement currentGuiElement, ITaskTreeNode parent, int parentIndex, int noOfTasks, ITaskTreeBuilder builder, ITaskTreeNodeFactory nodeFactory, RuleApplicationResult result) { List lastHierarchy = getGuiElementHierarchy(lastGuiElement); List currentHierarchy = getGuiElementHierarchy(currentGuiElement); int index = parentIndex; ITaskTreeNode generatedSequence = parent; for (int i = 0; i < lastHierarchy.size(); i++) { // add sequence for each element in the previous hierarchy, that does not occur in the // current hierarchy if ((currentHierarchy == null) || (i >= currentHierarchy.size()) || (!currentHierarchy.get(i).equals(lastHierarchy.get(i)))) { condenseTasksToSequence(generatedSequence, lastHierarchy.get(i), index, noOfTasks, builder, nodeFactory, result); // only in the first iteration, we condense tasks in the parent. Afterwards, // we always condense all tasks in the created sequence to a new subsequence. // Therefore, adapt all indexes appropriately generatedSequence = generatedSequence.getChildren().get(index - noOfTasks); index = noOfTasks; } } } /** * */ private boolean ensureSequencesRepresentingHierarchy(ITaskTreeNode parent, IGUIElement elementToRepresent, ITaskTreeBuilder builder, ITaskTreeNodeFactory nodeFactory, RuleApplicationResult result) { boolean applied = false; IGUIElement currentlyConsideredElement = elementToRepresent; IGUIElement elementRepresentedByParent = getGuiElement(parent); while ((currentlyConsideredElement != null) && (!currentlyConsideredElement.equals(elementRepresentedByParent))) { condenseTasksToSequence(parent, currentlyConsideredElement, parent.getChildren().size(), parent.getChildren().size(), builder, nodeFactory, result); applied = true; currentlyConsideredElement = currentlyConsideredElement.getParent(); } if (currentlyConsideredElement != null) { applied |= updateDescription(parent, currentlyConsideredElement, builder); } return applied; } /** * */ private RuleApplicationStatus condenseChildToSequencesRepresentingHierarchy(ITaskTreeNode parent, IGUIElement childGuiElement, IGUIElement parentGuiElement, int childIndex, ITaskTreeBuilder builder, ITaskTreeNodeFactory nodeFactory, RuleApplicationResult result) { ITaskTreeNode child = parent.getChildren().get(childIndex); boolean childIsSequence = child instanceof ISequence; boolean childHasGuiElement = childGuiElement != null; if (childIsSequence) { if (childHasGuiElement) { if (updateDescription(child, childGuiElement, builder)) { return RuleApplicationStatus.RULE_APPLICATION_FINISHED; } } } else { boolean applied = false; IGUIElement currentlyConsideredElement = childGuiElement; while ((currentlyConsideredElement != null) && (!currentlyConsideredElement.equals(parentGuiElement))) { condenseTasksToSequence(parent, currentlyConsideredElement, childIndex + 1, 1, builder, nodeFactory, result); applied = true; currentlyConsideredElement = currentlyConsideredElement.getParent(); } if (currentlyConsideredElement != null) { IGUIElement parentCommonDenominator = getGuiElement(parent); if (currentlyConsideredElement.equals(parentCommonDenominator)) { applied |= updateDescription(parent, currentlyConsideredElement, builder); } } if (applied) { return RuleApplicationStatus.RULE_APPLICATION_FINISHED; } } return null; } /** *

* condensed a subsequence of the children of the provided parent to a sequence. The * subsequence in the list of children defined by the index of the succeeding child * (parentIndex) and the number of tasks to be condensed (noOfTasks). *

* * @param parent the parent node whose children are to be condensed * @param target the GUI element in which all condensed interactions take place * @param parentIndex the index of the child in the parent, which follows the children * to be condensed * @param noOfTasks the number of children to be condensed * @param builder the builder to create the new subsequence * @param nodeFactory the node factory to instantiate the new subsequence * @param result the result of the rule application to store newly created parent nodes */ private void condenseTasksToSequence(ITaskTreeNode parent, IGUIElement target, int parentIndex, int noOfTasks, ITaskTreeBuilder builder, ITaskTreeNodeFactory nodeFactory, RuleApplicationResult result) { ISequence newSequence = nodeFactory.createNewSequence(); updateDescription(newSequence, target, builder); for (int i = 0; i < noOfTasks; i++) { builder.addChild(newSequence, parent.getChildren().get(parentIndex - noOfTasks)); // remove exactly the same number of children from the parent. builder.removeChild((ISequence) parent, parentIndex - noOfTasks); } builder.addChild((ISequence) parent, parentIndex - noOfTasks, newSequence); result.addNewlyCreatedParentNode(newSequence); } /** * */ private boolean updateDescription(ITaskTreeNode node, IGUIElement commonGuiElement, ITaskTreeBuilder builder) { String newDescription = "interactions on " + commonGuiElement.getStringIdentifier(); String currentDescription = node.getDescription(); if ((currentDescription == null) || (currentDescription.indexOf(newDescription) == -1)) { if ((currentDescription != null) && (!"".equals(currentDescription))) { newDescription = currentDescription + "; " + newDescription; } builder.setDescription(node, newDescription); return true; } else { return false; } } /** * */ private IGUIElement getGuiElement(ITaskTreeNode node) { if (node != null) { List terminalGuiElements = new ArrayList(); getTerminalGuiElements(node, terminalGuiElements); return getCommonDenominator(terminalGuiElements); } else { return null; } } /** * */ private void getTerminalGuiElements(ITaskTreeNode node, List terminalGuiElements) { if (node instanceof IEventTask) { if (((IEventTask) node).getEventTarget() instanceof IGUIElement) { IGUIElement terminalGuiElement = (IGUIElement) ((IEventTask) node).getEventTarget(); terminalGuiElement = searchHierarchyForGuiElementWithConsideredType(terminalGuiElement); if (terminalGuiElement != null) { terminalGuiElements.add(terminalGuiElement); } } } else { for (ITaskTreeNode child : node.getChildren()) { getTerminalGuiElements(child, terminalGuiElements); } } } /** * */ private List getGuiElementHierarchy(IGUIElement guiElement) { IGUIElement element = guiElement; if (!guiElementMatchesConsideredTypes(element)) { element = searchHierarchyForGuiElementWithConsideredType(element); } List hierarchy = new ArrayList(); while (element != null) { hierarchy.add(0, element); element = searchHierarchyForGuiElementWithConsideredType(element.getParent()); } if (hierarchy.size() > 0) { return hierarchy; } else { return null; } } /** * */ private IGUIElement searchHierarchyForGuiElementWithConsideredType(IGUIElement guiElement) { IGUIElement returnValue = guiElement; while ((returnValue != null) && !guiElementMatchesConsideredTypes(returnValue)) { returnValue = returnValue.getParent(); } return returnValue; } /** * */ private boolean guiElementsEqual(IGUIElement guiElement1, IGUIElement guiElement2) { if (guiElement1 == null) { return guiElement2 == null; } else { return guiElement1.equals(guiElement2); } } /** * */ private boolean isOnGuiElementPath(IGUIElement potentialPathElement, IGUIElement child) { IGUIElement guiElement = child; while (guiElement != null) { if (guiElement.equals(potentialPathElement)) { return true; } guiElement = guiElement.getParent(); } return false; } /** * */ private boolean guiElementMatchesConsideredTypes(IGUIElement guiElement) { if (guiElementFilter == null) { return true; } else { for (Class clazz : guiElementFilter) { if (clazz.isInstance(guiElement)) { return true; } } return false; } } /** * */ static class ReducableCommonDenominator { /** the GUI element being the common denominator */ IGUIElement commonGuiElement; /** the number of tasks that match the common denominator */ int noOfTasks; /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return noOfTasks + " tasks on " + commonGuiElement; } } }