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

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

improved debugging support

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