// 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.usability;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import de.ugoe.cs.autoquest.eventcore.guimodel.GUIElementGroup;
import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
/**
*
* TODO comment
*
*
* @author Patrick Harms
*/
class RuleUtils {
/**
*
*/
static Map> getGroups(Collection guiElements,
int maxDistToCommonParent)
{
Map> groups = new HashMap<>();
List guiElementsToGroup = new LinkedList<>(guiElements);
IGUIElement parentToGroup;
do {
List> sortedPaths = new LinkedList<>();
List commonParents = new LinkedList<>();
createSortedPaths
(guiElementsToGroup, sortedPaths, commonParents, maxDistToCommonParent);
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION
// Iterator> sortedPathsIt1 = sortedPaths.iterator();
// Iterator commonParentIt1 = commonParents.iterator();
//
// while (commonParentIt1.hasNext()) {
// IGUIElement currentParent = commonParentIt1.next();
// IGUIElement sortedPath = sortedPathsIt1.next().getLast();
//
// System.out.println(toPathString(sortedPath));
// System.out.println(toPathString(currentParent) + "###########");
// }
//
// System.out.println();
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION
parentToGroup = getParentToGroup(commonParents);
if (parentToGroup != null) {
Iterator> sortedPathsIt = sortedPaths.iterator();
ListIterator commonParentIt = commonParents.listIterator();
while (commonParentIt.hasNext()) {
if (!parentToGroup.equals(commonParentIt.next())) {
sortedPathsIt.next();
}
else {
break;
}
}
List groupedGUIElements = new LinkedList<>();
// go one backward to ensure, that next will return the first occurrence
// of the parent to create the group for.
commonParentIt.previous();
do {
IGUIElement guiElementToGroup = sortedPathsIt.next().getLast();
groupedGUIElements.add(guiElementToGroup);
guiElementsToGroup.remove(guiElementToGroup);
}
while (parentToGroup.equals(commonParentIt.next()));
groups.put(parentToGroup, groupedGUIElements);
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION
// System.out.println("group for");
// System.out.println(toPathString(parentToGroup));
//
// for (IGUIElement element : groupedGUIElements) {
// System.out.println(toPathString(element));
// }
//
// System.out.println();
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION
}
}
while (parentToGroup != null);
return groups;
}
/**
*
*/
static String toPathString(IGUIElement element) {
IGUIElement parent = element;
String result = "";
while (parent != null) {
if (!(parent instanceof GUIElementGroup)) {
result = parent.toString() + "/" + result;
}
else {
result = parent.toString().hashCode() + "/" + result;
}
parent = parent.getParent();
}
return result.toString();
}
/**
*
*/
private static void createSortedPaths(List guiElements,
List> sortedPaths,
List commonParents,
int maxDistToCommonParent)
{
for (IGUIElement guiElement : guiElements) {
// create the path
LinkedList path = new LinkedList<>();
IGUIElement parent = guiElement;
while (parent != null) {
if (!(parent instanceof GUIElementGroup)) {
path.addFirst(parent);
}
parent = parent.getParent();
}
// sort it into the list of paths
int maxEquality = 0;
int maxEqualityPos = 0;
int pos = 0;
for (List candidate : sortedPaths) {
int equality = 0;
while ((equality < candidate.size()) && (equality < path.size()) &&
(candidate.get(equality).equals(path.get(equality))))
{
equality++;
}
if (equality > maxEquality) {
maxEquality = equality;
maxEqualityPos = pos;
}
pos++;
}
sortedPaths.add(maxEqualityPos, path);
if ((maxEquality > 0) && ((path.size() - maxEquality) < maxDistToCommonParent)) {
commonParents.add(maxEqualityPos, path.get(maxEquality - 1));
}
else {
commonParents.add(maxEqualityPos, null);
}
}
}
/**
*
*/
private static IGUIElement getParentToGroup(List commonParents) {
Map occurrenceCounts = new HashMap<>();
Map depths = new HashMap<>();
// get the required information about path lengths and occurrence counts
for (IGUIElement commonParent : commonParents) {
Integer occurrenceCount = occurrenceCounts.get(commonParent);
if (occurrenceCount != null) {
occurrenceCounts.put(commonParent, occurrenceCount + 1);
}
else {
occurrenceCounts.put(commonParent, 1);
}
if (!depths.containsKey(commonParent)) {
int depth = 0;
IGUIElement parent = commonParent;
while (parent != null) {
depth++;
parent = parent.getParent();
}
depths.put(commonParent, depth);
}
}
IGUIElement elementToGroup = null;
// get the GUI element being the parent most often
for (IGUIElement commonParent : commonParents) {
if (elementToGroup == null) {
elementToGroup = commonParent;
}
else if ((commonParent != null) &&
(!elementToGroup.equals(commonParent)))
{
int occurrenceCountCandidate = occurrenceCounts.get(commonParent);
int depthCandidate = depths.get(commonParent);
int occurrenceCountElement = occurrenceCounts.get(elementToGroup);
int depthElement = depths.get(elementToGroup);
if ((depthCandidate > depthElement) ||
((depthCandidate == depthElement) &&
(occurrenceCountCandidate > occurrenceCountElement)))
{
elementToGroup = commonParent;
}
else if ((occurrenceCountCandidate == occurrenceCountElement) &&
(depthCandidate == depthElement))
{
// in this situation, the order is irrelevant. The GUI elements for which
// both paths were identified as parents are completely different.
// Otherwise, they would have the same parent. But as they are that
// different, they will not occur subsequently in the ordered list
// of GUI elements to group. Hence, we just reuse the one the is currently
// identified as the one to group next.
}
}
}
return elementToGroup;
}
/**
*
*/
private RuleUtils() {
// prevent instantiation
}
}