// Module : $RCSfile: GUIModel.java,v $
// Version : $Revision: 0.0 $ $Author: pharms $ $Date: 14.08.2012 $
// Project : quest-core-events
// Creation : 2012 by pharms
// Copyright : Patrick Harms, 2012
package de.ugoe.cs.quest.eventcore.guimodel;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
*
* The goal of a GUI model is to correctly l
*
*
* @version $Revision: $ $Date: 14.08.2012$
* @author 2012, last modified by $Author: pharms$
*/
public class GUIModel {
/**
*
*/
private TreeNode root = new TreeNode();
/**
*
*/
private List allNodes = new ArrayList();
/**
* TODO: comment
*
* @param currentGUIElementPath
* @return
* @throws GUIModelException
*/
public IGUIElement integratePath(List extends IGUIElementSpec> guiElementPath,
IGUIElementFactory guiElementFactory)
throws GUIModelException
{
if ((guiElementPath == null) || (guiElementPath.size() <= 0)) {
throw new IllegalArgumentException
("GUI element path must contain at least one element");
}
List remainingPath = new LinkedList();
for (IGUIElementSpec spec : guiElementPath)
{
remainingPath.add(spec);
}
return integratePath(root, remainingPath, guiElementFactory);
}
/**
* TODO: comment
*
* @param root
* @return
*/
public List getChildren(IGUIElement guiElement) {
for (TreeNode node : allNodes) {
if (node.guiElement.equals(guiElement)) {
List result = new ArrayList();
if (node.children != null) {
for (TreeNode child : node.children) {
result.add(child.guiElement);
}
}
return result;
}
}
return null;
}
/**
*
* TODO: comment
*
*
* @param guiElement
* @return
*/
public IGUIElement getParent(IGUIElement guiElement) {
for (TreeNode node : allNodes) {
for (TreeNode child : node.children) {
if (child.guiElement.equals(guiElement)) {
return node.guiElement;
}
}
}
return null;
}
/**
* TODO: comment
*
* @return
*/
public List getRootElements() {
List roots = new ArrayList();
if (root.children != null) {
for (TreeNode rootChild : root.children) {
roots.add(rootChild.guiElement);
}
}
return roots;
}
/**
*
* TODO: comment
*
*
* @param root2
* @param guiElementPath
* @param guiElementFactory
* @return
* @throws GUIModelException
*/
private IGUIElement integratePath(TreeNode parentNode,
List extends IGUIElementSpec> remainingPath,
IGUIElementFactory guiElementFactory)
throws GUIModelException
{
IGUIElementSpec specToIntegrateElementFor = remainingPath.remove(0);
List similarNodes =
determineSimilarChildNodes(parentNode, specToIntegrateElementFor);
if (similarNodes.size() > 1) {
// this may happen, if the GUI elements changed over time (e.g. their name is usually
// set later in the program execution) and if they now match because of the changes.
// So perform a merge of all similar children of the current parent node to reduce the
// model and then try the determination of matching children again.
mergeSimilarChildren(parentNode);
similarNodes = determineSimilarChildNodes(parentNode, specToIntegrateElementFor);
if (similarNodes.size() > 1) {
// this can happen, because the similarity check is not transitive. The new GUI
// element may be similar to two or more existing ones, but the existing ones
// may not be similar to each other. As an example, the new GUI element may
// not yet provide sufficient information (such as all names it will have during
// the execution of the program). Therefore the similarity check with GUI elements
// that already contain more information may return true. But the similarity check
// of two GUI elements that already carry a lot of information may return false,
// although both are similar to the new GUI element. Therefore, we try a selection
// based on the children of the existing and new GUI elements. The one for which
// the existing children match best is selected to be the right one.
similarNodes =
considerSubChildren(similarNodes, specToIntegrateElementFor, remainingPath);
if (similarNodes.size() > 1) {
System.out.println("TODO: implement handling to many similar children: " +
specToIntegrateElementFor);
for (TreeNode similarNode : similarNodes) {
System.out.println(" " + similarNode.guiElement);
}
System.out.println();
}
}
}
else if (similarNodes.size() == 1) {
similarNodes.get(0).guiElement.updateSpecification(specToIntegrateElementFor);
}
else if (similarNodes.size() == 0) {
// if we get here, the corresponding path does not exist yet. So create it
IGUIElement newElement = guiElementFactory.instantiateGUIElement
(specToIntegrateElementFor, parentNode.guiElement);
similarNodes.add(parentNode.addChild(newElement));
}
if (remainingPath.size() > 0) {
return integratePath(similarNodes.get(0), remainingPath, guiElementFactory);
}
else {
return similarNodes.get(0).guiElement;
}
}
/**
*
* TODO: comment
*
*
* @param parentNode
* @param specToIntegrateElementFor
* @return
*/
private List determineSimilarChildNodes(TreeNode parentNode,
IGUIElementSpec specToMatch)
{
List similarChildren = new ArrayList();
if (parentNode.children != null) {
for (TreeNode child : parentNode.children) {
if (specToMatch.getSimilarity(child.guiElement.getSpecification())) {
similarChildren.add(child);
}
}
}
return similarChildren;
}
/**
*
* TODO: comment
*
*
* @param similarChildren
* @param remove
* @return
*/
private List considerSubChildren(List similarNodes,
IGUIElementSpec specToMatch,
List extends IGUIElementSpec> remainingPath)
{
List reducedList = new ArrayList();
// check, if there are any children to consider and remove any similar node, that has
// further children
if (remainingPath.size() <= 0) {
for (TreeNode similarNode : similarNodes) {
if ((similarNode.children == null) || (similarNode.children.size() == 0)) {
reducedList.add(similarNode);
}
}
}
else {
// if there are further children to consider, then check if there is already a child
// node that has an appropriate child
IGUIElementSpec subChildSpec = remainingPath.get(0);
for (TreeNode similarNode : similarNodes) {
if (similarNode.children != null) {
for (TreeNode subchild : similarNode.children) {
if (subchild.guiElement.getSpecification().getSimilarity(subChildSpec)) {
reducedList.add(similarNode);
break;
}
}
}
}
}
return reducedList;
}
/**
*
* TODO: comment
*
*
* @param parentNode
*/
private void mergeSimilarChildren(TreeNode parentNode) {
boolean performedMerge;
do {
performedMerge = false;
if (parentNode.children != null) {
for (int i = 0; (!performedMerge) && (i < parentNode.children.size()); i++) {
for (int j = i + 1; j < parentNode.children.size(); j++) {
IGUIElement elem1 = parentNode.children.get(i).guiElement;
IGUIElement elem2 = parentNode.children.get(j).guiElement;
if (elem1.getSpecification().getSimilarity(elem2.getSpecification())) {
TreeNode replacement = mergeTreeNodes(parentNode.children.get(i),
parentNode.children.get(j));
parentNode.children.set(i, replacement);
parentNode.children.remove(j);
performedMerge = true;
break;
}
}
}
}
}
while (performedMerge);
}
/**
*
* TODO: comment
*
*
* @param treeNode
* @param treeNode2
* @return
*/
private TreeNode mergeTreeNodes(TreeNode treeNode1, TreeNode treeNode2) {
TreeNode replacement = new TreeNode();
replacement.guiElement = treeNode1.guiElement;
// the following two lines are needed to preserve the references to the existing GUI
// elements. If two elements are the same, one should be deleted to make the elements
// singletons again. However, there may exist references to both objects. To preserve
// these, we simply register the equal GUI elements with each other so that an equals
// check can return true.
replacement.guiElement.addEqualGUIElement(treeNode2.guiElement);
treeNode2.guiElement.addEqualGUIElement(replacement.guiElement);
if (treeNode1.children != null) {
for (TreeNode child : treeNode1.children) {
replacement.addChild(child.guiElement);
}
}
if (treeNode2.children != null) {
for (TreeNode child : treeNode2.children) {
replacement.addChild(child.guiElement);
}
}
mergeSimilarChildren(replacement);
replacement.guiElement.updateSpecification(treeNode2.guiElement.getSpecification());
return replacement;
}
/**
*
* TODO comment
*
*
* @version $Revision: $ $Date: 17.08.2012$
* @author 2012, last modified by $Author: pharms$
*/
private class TreeNode
{
/** */
private IGUIElement guiElement;
/** */
private List children;
/** */
//private TreeNode parent;
/**
*
* TODO: comment
*
*
* @param guiElement
* @return
*/
private TreeNode addChild(IGUIElement guiElement)
{
if (children == null)
{
children = new ArrayList();
}
TreeNode child = new TreeNode();
child.guiElement = guiElement;
//child.parent = this;
children.add(child);
allNodes.add(child);
return child;
}
}
}