source: trunk/quest-core-events/src/main/java/de/ugoe/cs/quest/eventcore/guimodel/GUIModel.java @ 612

Last change on this file since 612 was 611, checked in by pharms, 12 years ago
  • improved detection of similar nodes
  • implemented merging of subtrees in the case that the roots of both subtrees are identified as similar
File size: 12.4 KB
Line 
1// Module    : $RCSfile: GUIModel.java,v $
2// Version   : $Revision: 0.0 $  $Author: pharms $  $Date: 14.08.2012 $
3// Project   : quest-core-events
4// Creation  : 2012 by pharms
5// Copyright : Patrick Harms, 2012
6
7package de.ugoe.cs.quest.eventcore.guimodel;
8
9import java.util.ArrayList;
10import java.util.LinkedList;
11import java.util.List;
12
13/**
14 * <p>
15 * The goal of a GUI model is to correctly l
16 * </p>
17 *
18 * @version $Revision: $ $Date: 14.08.2012$
19 * @author 2012, last modified by $Author: pharms$
20 */
21public class GUIModel {
22   
23    /**
24     *
25     */
26    private TreeNode root = new TreeNode();
27   
28    /**
29     *
30     */
31    private List<TreeNode> allNodes = new ArrayList<TreeNode>();
32
33    /**
34     * TODO: comment
35     *
36     * @param currentGUIElementPath
37     * @return
38     * @throws GUIModelException
39     */
40    public IGUIElement integratePath(List<? extends IGUIElementSpec> guiElementPath,
41                                     IGUIElementFactory              guiElementFactory)
42        throws GUIModelException
43    {
44        if ((guiElementPath == null) || (guiElementPath.size() <= 0)) {
45            throw new IllegalArgumentException
46                ("GUI element path must contain at least one element");
47        }
48       
49        List<IGUIElementSpec> remainingPath = new LinkedList<IGUIElementSpec>();
50       
51        for (IGUIElementSpec spec : guiElementPath)
52        {
53            remainingPath.add(spec);
54        }
55       
56        return integratePath(root, remainingPath, guiElementFactory);
57    }
58
59    /**
60     * TODO: comment
61     *
62     * @param root
63     * @return
64     */
65    public List<IGUIElement> getChildren(IGUIElement guiElement) {
66        for (TreeNode node : allNodes) {
67            if (node.guiElement.equals(guiElement)) {
68                List<IGUIElement> result = new ArrayList<IGUIElement>();
69               
70                if (node.children != null) {
71                    for (TreeNode child : node.children) {
72                      result.add(child.guiElement);
73                    }
74                }
75               
76                return result;
77            }
78        }
79       
80        return null;
81    }
82
83    /**
84     * <p>
85     * TODO: comment
86     * </p>
87     *
88     * @param guiElement
89     * @return
90     */
91    public IGUIElement getParent(IGUIElement guiElement) {
92        for (TreeNode node : allNodes) {
93            for (TreeNode child : node.children) {
94                if (child.guiElement.equals(guiElement)) {
95                    return node.guiElement;
96                }
97            }
98        }
99       
100        return null;
101    }
102
103    /**
104     * TODO: comment
105     *
106     * @return
107     */
108    public List<IGUIElement> getRootElements() {
109        List<IGUIElement> roots = new ArrayList<IGUIElement>();
110       
111        if (root.children != null) {
112            for (TreeNode rootChild : root.children) {
113                roots.add(rootChild.guiElement);
114            }
115        }
116       
117        return roots;
118    }
119
120    /**
121     * <p>
122     * TODO: comment
123     * </p>
124     *
125     * @param root2
126     * @param guiElementPath
127     * @param guiElementFactory
128     * @return
129     * @throws GUIModelException
130     */
131    private IGUIElement integratePath(TreeNode                        parentNode,
132                                      List<? extends IGUIElementSpec> remainingPath,
133                                      IGUIElementFactory              guiElementFactory)
134        throws GUIModelException
135    {
136        IGUIElementSpec specToIntegrateElementFor = remainingPath.remove(0);
137
138        List<TreeNode> similarNodes =
139            determineSimilarChildNodes(parentNode, specToIntegrateElementFor);
140       
141        if (similarNodes.size() > 1) {
142            // this may happen, if the GUI elements changed over time (e.g. their name is usually
143            // set later in the program execution) and if they now match because of the changes.
144            // So perform a merge of all similar children of the current parent node to reduce the
145            // model and then try the determination of matching children again.
146            mergeSimilarChildren(parentNode);
147            similarNodes = determineSimilarChildNodes(parentNode, specToIntegrateElementFor);
148           
149            if (similarNodes.size() > 1) {
150                // this can happen, because the similarity check is not transitive. The new GUI
151                // element may be similar to two or more existing ones, but the existing ones
152                // may not be similar to each other. As an example, the new GUI element may
153                // not yet provide sufficient information (such as all names it will have during
154                // the execution of the program). Therefore the similarity check with GUI elements
155                // that already contain more information may return true. But the similarity check
156                // of two GUI elements that already carry a lot of information may return false,
157                // although both are similar to the new GUI element. Therefore, we try a selection
158                // based on the children of the existing and new GUI elements. The one for which
159                // the existing children match best is selected to be the right one.
160                similarNodes =
161                    considerSubChildren(similarNodes, specToIntegrateElementFor, remainingPath);
162
163                if (similarNodes.size() > 1) {
164                    System.out.println("TODO: implement handling to many similar children: " +
165                                       specToIntegrateElementFor);
166                    for (TreeNode similarNode : similarNodes) {
167                        System.out.println("    " + similarNode.guiElement);
168                    }
169                    System.out.println();
170                }
171            }
172        }
173        else if (similarNodes.size() == 1) {
174            similarNodes.get(0).guiElement.updateSpecification(specToIntegrateElementFor);
175        }
176        else if (similarNodes.size() == 0) {
177            // if we get here, the corresponding path does not exist yet. So create it
178            IGUIElement newElement = guiElementFactory.instantiateGUIElement
179                (specToIntegrateElementFor, parentNode.guiElement);
180           
181            similarNodes.add(parentNode.addChild(newElement));
182        }
183       
184        if (remainingPath.size() > 0) {
185            return integratePath(similarNodes.get(0), remainingPath, guiElementFactory);
186        }
187        else {
188            return similarNodes.get(0).guiElement;
189        }
190    }
191
192    /**
193     * <p>
194     * TODO: comment
195     * </p>
196     *
197     * @param parentNode
198     * @param specToIntegrateElementFor
199     * @return
200     */
201    private List<TreeNode> determineSimilarChildNodes(TreeNode        parentNode,
202                                                      IGUIElementSpec specToMatch)
203    {
204        List<TreeNode> similarChildren = new ArrayList<TreeNode>();
205       
206        if (parentNode.children != null) {
207            for (TreeNode child : parentNode.children) {
208                if (specToMatch.getSimilarity(child.guiElement.getSpecification())) {
209                    similarChildren.add(child);
210                }
211            }
212        }
213       
214        return similarChildren;
215    }
216
217    /**
218     * <p>
219     * TODO: comment
220     * </p>
221     *
222     * @param similarChildren
223     * @param remove
224     * @return
225     */
226    private List<TreeNode> considerSubChildren(List<TreeNode>                  similarNodes,
227                                               IGUIElementSpec                 specToMatch,
228                                               List<? extends IGUIElementSpec> remainingPath)
229    {
230        List<TreeNode> reducedList = new ArrayList<TreeNode>();
231       
232        // check, if there are any children to consider and remove any similar node, that has
233        // further children
234        if (remainingPath.size() <= 0) {
235            for (TreeNode similarNode : similarNodes) {
236                if ((similarNode.children == null) || (similarNode.children.size() == 0)) {
237                    reducedList.add(similarNode);
238                }
239            }
240        }
241        else {
242            // if there are further children to consider, then check if there is already a child
243            // node that has an appropriate child
244            IGUIElementSpec subChildSpec = remainingPath.get(0);
245            for (TreeNode similarNode : similarNodes) {
246                if (similarNode.children != null) {
247                    for (TreeNode subchild : similarNode.children) {
248                        if (subchild.guiElement.getSpecification().getSimilarity(subChildSpec)) {
249                            reducedList.add(similarNode);
250                            break;
251                        }
252                    }
253                }
254            }
255        }
256       
257        return reducedList;
258    }
259
260    /**
261     * <p>
262     * TODO: comment
263     * </p>
264     *
265     * @param parentNode
266     */
267    private void mergeSimilarChildren(TreeNode parentNode) {
268        boolean performedMerge;
269       
270        do {
271            performedMerge = false;
272            if (parentNode.children != null) {
273                for (int i = 0; (!performedMerge) && (i < parentNode.children.size()); i++) {
274                    for (int j = i + 1; j < parentNode.children.size(); j++) {
275                        IGUIElement elem1 = parentNode.children.get(i).guiElement;
276                        IGUIElement elem2 = parentNode.children.get(j).guiElement;
277                        if (elem1.getSpecification().getSimilarity(elem2.getSpecification())) {
278                            TreeNode replacement = mergeTreeNodes(parentNode.children.get(i),
279                                                                  parentNode.children.get(j));
280                           
281                            parentNode.children.set(i, replacement);
282                            parentNode.children.remove(j);
283                            performedMerge = true;
284                            break;
285                        }
286                    }
287                }
288            }
289        }
290        while (performedMerge);
291    }
292
293    /**
294     * <p>
295     * TODO: comment
296     * </p>
297     *
298     * @param treeNode
299     * @param treeNode2
300     * @return
301     */
302    private TreeNode mergeTreeNodes(TreeNode treeNode1, TreeNode treeNode2) {
303        TreeNode replacement = new TreeNode();
304       
305        replacement.guiElement = treeNode1.guiElement;
306       
307        // the following two lines are needed to preserve the references to the existing GUI
308        // elements. If two elements are the same, one should be deleted to make the elements
309        // singletons again. However, there may exist references to both objects. To preserve
310        // these, we simply register the equal GUI elements with each other so that an equals
311        // check can return true.
312        replacement.guiElement.addEqualGUIElement(treeNode2.guiElement);
313        treeNode2.guiElement.addEqualGUIElement(replacement.guiElement);
314       
315        if (treeNode1.children != null) {
316            for (TreeNode child : treeNode1.children) {
317                replacement.addChild(child.guiElement);
318            }
319        }
320       
321        if (treeNode2.children != null) {
322            for (TreeNode child : treeNode2.children) {
323                replacement.addChild(child.guiElement);
324            }
325        }
326       
327        mergeSimilarChildren(replacement);
328       
329        replacement.guiElement.updateSpecification(treeNode2.guiElement.getSpecification());
330       
331        return replacement;
332    }
333
334    /**
335     * <p>
336     * TODO comment
337     * </p>
338     *
339     * @version $Revision: $ $Date: 17.08.2012$
340     * @author 2012, last modified by $Author: pharms$
341     */
342    private class TreeNode
343    {
344        /** */
345        private IGUIElement guiElement;
346       
347        /** */
348        private List<TreeNode> children;
349       
350        /** */
351        //private TreeNode parent;
352       
353        /**
354         * <p>
355         * TODO: comment
356         * </p>
357         *
358         * @param guiElement
359         * @return
360         */
361        private TreeNode addChild(IGUIElement guiElement)
362        {
363            if (children == null)
364            {
365                children = new ArrayList<TreeNode>();
366            }
367           
368            TreeNode child = new TreeNode();
369            child.guiElement = guiElement;
370            //child.parent = this;
371            children.add(child);
372           
373            allNodes.add(child);
374           
375            return child;
376        }
377    }
378}
Note: See TracBrowser for help on using the repository browser.