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

Last change on this file since 643 was 643, checked in by pharms, 12 years ago

implemented dumping a GUI model to a stream

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