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

Last change on this file since 1181 was 1181, checked in by pharms, 11 years ago
  • remove a find bugs warning
File size: 28.2 KB
RevLine 
[927]1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
[820]14
[922]15package de.ugoe.cs.autoquest.eventcore.guimodel;
[545]16
[643]17import java.io.OutputStream;
18import java.io.PrintStream;
[1116]19import java.io.Serializable;
[746]20import java.io.UnsupportedEncodingException;
[545]21import java.util.ArrayList;
[603]22import java.util.LinkedList;
[545]23import java.util.List;
[1086]24import java.util.Stack;
[821]25import java.util.logging.Level;
[545]26
[821]27import de.ugoe.cs.util.console.Console;
28
[545]29/**
[576]30 * <p>
[746]31 * A GUI model is a tree of {@link IGUIElements} and represents a complete GUI of a software. It is
32 * platform independent. It may have several root nodes, as some GUIs are made up of several Frames
33 * being independent from each other. The GUI model is filled using the
[820]34 * {@link #integratePath(List, IGUIElementFactory)} method.
[576]35 * </p>
[545]36 *
[821]37 * @version 1.0
[820]38 * @author Patrick Harms, Steffen Herbold
[545]39 */
[1116]40public class GUIModel implements Serializable {
[820]41
[1116]42    /**  */
43    private static final long serialVersionUID = 1L;
44
[545]45    /**
[746]46     * <p>
[831]47     * The root node of the tree not provided externally.
[746]48     * </p>
[545]49     */
[576]50    private TreeNode root = new TreeNode();
[820]51
[545]52    /**
[746]53     * <p>
[831]54     * A list with all nodes currently known
[746]55     * </p>
[545]56     */
[576]57    private List<TreeNode> allNodes = new ArrayList<TreeNode>();
[545]58
59    /**
[746]60     * <p>
61     * Integrates a path of GUI elements into the GUI model. The GUI model itself is a tree and
62     * therefore a set of different paths through the tree that start with a root node and end with
63     * a leaf node. Such a path can be added to the tree. The method checks, if any of the GUI
64     * elements denoted by the path already exists. If so, it reuses it. It may therefore also
[820]65     * return an existing GUI element being the leaf node of the provided path. If a GUI element of
66     * the path does not exist yet, it creates a new one using the provided GUI element factory.
[746]67     * </p>
68     * <p>
[820]69     * If a GUI element specification describes an existing GUI element or not is determined through
70     * comparing the GUI element specifications of the existing GUI elements with the ones provided
71     * in the path. The comparison is done using the
72     * {@link IGUIElementSpec#getSimilarity(IGUIElementSpec)} method. The comparison is only done on
73     * the correct levels. I.e. the currently known root elements of the tree are only compared to
74     * the first element in the path. If the correct one is found or created, its children are
[746]75     * compared only to the second specification in the path, and so on.
76     * </p>
77     * <p>
78     * The returned GUI elements are singletons. I.e. it is tried to return always the identical
[820]79     * object for the same denoted element. However, while creating the GUI model, the similarity of
80     * GUI elements may change. Therefore, the method might determine, that two formerly different
81     * nodes are now similar. (This may happen, e.g. if GUI elements do not have initial names which
82     * are set afterwards. Therefore, first they are handled differently and later they can be
83     * identified as being the same.) In such a case, there are already several GUI element objects
84     * instantiated for the same GUI element. The singleton paradigm gets broken. Therefore, such
85     * GUI element objects are registered with each other, so that their equal method can determine
86     * equality again correctly, although the objects are no singletons anymore.
[746]87     * </p>
[820]88     *
89     * @param guiElementPath
90     *            the path to integrate into the model
91     * @param guiElementFactory
92     *            the GUI element factory to be used for instantiating GUI element objects
93     *
[746]94     * @return The GUI element object representing the GUI element denoted by the provided path
95     *
[820]96     * @throws GUIModelException
97     *             thrown in cases such as the GUI element object could not be instantiated
98     * @throws IllegalArgumentException
99     *             if the provided path is invalid.
[545]100     */
[576]101    public IGUIElement integratePath(List<? extends IGUIElementSpec> guiElementPath,
[820]102                                     IGUIElementFactory guiElementFactory)
[746]103        throws GUIModelException, IllegalArgumentException
[576]104    {
[611]105        if ((guiElementPath == null) || (guiElementPath.size() <= 0)) {
[820]106            throw new IllegalArgumentException("GUI element path must contain at least one element");
[611]107        }
[820]108
[603]109        List<IGUIElementSpec> remainingPath = new LinkedList<IGUIElementSpec>();
[820]110
111        for (IGUIElementSpec spec : guiElementPath) {
[576]112            remainingPath.add(spec);
[545]113        }
[820]114
[576]115        return integratePath(root, remainingPath, guiElementFactory);
[545]116    }
117
118    /**
[746]119     * <p>
[820]120     * Returns all children of the provided GUI element or null, if it does not have any or the node
121     * is unknown.
[746]122     * </p>
123     *
[820]124     * @param guiElement
125     *            the GUI element of which the children shall be returned
126     *
[746]127     * @return As described
[545]128     */
[576]129    public List<IGUIElement> getChildren(IGUIElement guiElement) {
[821]130        List<IGUIElement> result = null;
[576]131        for (TreeNode node : allNodes) {
132            if (node.guiElement.equals(guiElement)) {
[821]133                if (result == null) {
134                    result = new ArrayList<IGUIElement>();
[820]135
[821]136                    if (node.children != null) {
137                        for (TreeNode child : node.children) {
138                            result.add(child.guiElement);
139                        }
[576]140                    }
141                }
[821]142                else {
143                    Console
144                        .traceln(Level.SEVERE,
145                                 "Multiple nodes in the internal GUI model match the same GUI element. "
146                                     + "This should not be the case and the GUI model is probably invalid.");
147                }
[576]148            }
149        }
[820]150
[821]151        return result;
[545]152    }
153
154    /**
[593]155     * <p>
[746]156     * Returns the parent GUI element of the provided GUI element or null, if it does not have a
157     * parent (i.e. if it is a root node) or if the node is unknown.
[593]158     * </p>
[746]159     *
[820]160     * @param guiElement
161     *            the GUI element of which the parent shall be returned
162     *
[746]163     * @return As described
[545]164     */
[593]165    public IGUIElement getParent(IGUIElement guiElement) {
[821]166        IGUIElement parent = null;
167
[593]168        for (TreeNode node : allNodes) {
[1088]169            if (node.children != null) {
170                for (TreeNode child : node.children) {
171                    if (child.guiElement.equals(guiElement)) {
172                        if (parent != null) {
173                            parent = node.guiElement;
174                        }
175                        else {
176                            Console
[821]177                            .traceln(Level.SEVERE,
178                                     "Multiple nodes in the internal GUI model match the same GUI element. "
[1088]179                                             + "This should not be the case and the GUI model is probably invalid.");
180                        }
[821]181                    }
[593]182                }
183            }
184        }
[820]185
[821]186        return parent;
[593]187    }
188
189    /**
[746]190     * <p>
191     * Returns all root GUI elements of the model or an empty list, if the model is empty
192     * </p>
[820]193     *
[746]194     * @return As described
[593]195     */
[576]196    public List<IGUIElement> getRootElements() {
197        List<IGUIElement> roots = new ArrayList<IGUIElement>();
[820]198
[611]199        if (root.children != null) {
200            for (TreeNode rootChild : root.children) {
201                roots.add(rootChild.guiElement);
202            }
[545]203        }
[820]204
[545]205        return roots;
206    }
[1086]207   
208    /**
209     * returns a traverser for the GUI model to have efficient access to the tree of GUI elements
210     * without having direct access.
211     *
212     * @return a traverser
213     */
214    public Traverser getTraverser() {
215        return new Traverser();
216    }
[545]217
218    /**
[1088]219     * returns a traverser for the GUI model starting at the given GUI element. Returns null, if
220     * the GUI element is not part of the model.
221     *
222     * @return a traverser
223     */
224    public Traverser getTraverser(IGUIElement startingAt) {
225        TreeNode node = findNode(startingAt);
226       
227        if (node != null) {
228            Traverser traverser = new Traverser();
229            traverser.navigateTo(node);
230            return traverser;
231        }
232        else {
233            return null;
234        }
235    }
236
237    /**
[746]238     * <p>
239     * dumps the GUI model to the provided stream. Each node is represented through its toString()
240     * method. If a node has children, those are dumped indented and surrounded by braces.
241     * </p>
242     *
[820]243     * @param out
244     *            The stream to dump the textual representation of the model to
245     * @param encoding
246     *            The encoding to be used while dumping
[643]247     */
[746]248    public void dump(OutputStream out, String encoding) {
[643]249        PrintStream stream;
[820]250
[643]251        if (out instanceof PrintStream) {
252            stream = (PrintStream) out;
253        }
254        else {
[746]255            String enc = encoding == null ? "UTF-8" : encoding;
256            try {
257                stream = new PrintStream(out, true, enc);
258            }
259            catch (UnsupportedEncodingException e) {
[747]260                throw new IllegalArgumentException("encodind " + enc + " not supported");
[746]261            }
[643]262        }
[820]263
[1086]264        for (TreeNode node : root.children) {
265            dumpGUIElement(stream, node, "");
[643]266        }
267    }
268
269    /**
[576]270     * <p>
[821]271     * By calling this method, the GUIModel is traversed and similar nodes are merged.
[576]272     * </p>
[820]273     *
274     */
275    public void condenseModel() {
[821]276        mergeSubTree(root);
[820]277    }
[846]278   
279    /**
280     * <p>
281     * Merges the tree nodes of two GUI elements. The GUI elements need to have the same parent.
282     * </p>
283     *
284     * @param guiElement1
285     *            the first merge GUI element
286     * @param guiElement2
287     *            the second merge GUI element
288     * @throws IllegalArgumentException
289     *             thrown if the two GUI elements do not have the same parent
290     */
291    public void mergeGUIElements(IGUIElement guiElement1, IGUIElement guiElement2)
292        throws IllegalArgumentException
293    {
294        // check if both nodes have the same parent
295        IGUIElement parentElement = guiElement1.getParent();
296        if (parentElement != null && !parentElement.equals(guiElement2.getParent())) {
297            throw new IllegalArgumentException("can only merge nodes with the same parent");
298        }
[820]299
[846]300        // get the TreeNode of the parent of the GUI elements
301        TreeNode parent = findNode(parentElement);
302
303        // get the TreeNodes for both GUI elements
304        TreeNode node1 = findNode(guiElement1);
305        TreeNode node2 = findNode(guiElement2);
306
307        if (node1 == null || node2 == null) {
308            throw new IllegalArgumentException(
309                                               "Error while merging nodes: one element is not part of the GUI model!");
310        }
311
312        TreeNode replacement = mergeTreeNodes(node1, node2);
313
314        if (parent != null) {
315            // remove node1 and node2 from the parent's children and add the replacement instead
316            // assumes that there are no duplicates of node1 and node2
317            if (parent.children != null) {
318                parent.children.set(parent.children.indexOf(node1), replacement);
319                parent.children.remove(node2);
320            }
321        }
322
323    }
324
[820]325    /**
326     * <p>
327     * internally integrates a path as the children of the provided parent node. This method is
328     * recursive and calls itself, for the child of the parent node, that matches the first element
329     * in the remaining path.
330     * </p>
331     *
332     * @param parentNode
333     *            the parent node to add children for
334     * @param guiElementPath
335     *            the path of children to be created starting with the parent node
336     * @param guiElementFactory
337     *            the GUI element factory to be used for instantiating GUI element objects
338     *
[746]339     * @return The GUI element object representing the GUI element denoted by the provided path
340     *
[820]341     * @throws GUIModelException
342     *             thrown in cases such as the GUI element object could not be instantiated
[545]343     */
[820]344    private IGUIElement integratePath(TreeNode parentNode,
[576]345                                      List<? extends IGUIElementSpec> remainingPath,
[820]346                                      IGUIElementFactory guiElementFactory)
[576]347        throws GUIModelException
348    {
349        IGUIElementSpec specToIntegrateElementFor = remainingPath.remove(0);
[611]350
[820]351        TreeNode child = findEqualChild(parentNode, specToIntegrateElementFor);
[821]352        if (child == null) {
[820]353            IGUIElement newElement =
354                guiElementFactory.instantiateGUIElement(specToIntegrateElementFor,
355                                                        parentNode.guiElement);
[611]356
[820]357            child = parentNode.addChild(newElement);
[1181]358            allNodes.add(child);
[545]359        }
[820]360
[576]361        if (remainingPath.size() > 0) {
[820]362            return integratePath(child, remainingPath, guiElementFactory);
[576]363        }
364        else {
[820]365            return child.guiElement;
[576]366        }
[545]367    }
368
[576]369    /**
370     * <p>
[821]371     * Searches the children of a tree node to see if the {@link IGUIElementSpec} of equals the
372     * specification of the {@link TreeNode#guiElement} of the child. If a match is found, the child
373     * is returned.
[611]374     * </p>
[831]375     *
376     * @param parentNode
377     *            parent node whose children are searched
378     * @param specToMatch
379     *            specification that is searched for
380     * @return matching child node or null if no child matches
[611]381     */
[820]382    private TreeNode findEqualChild(TreeNode parentNode, IGUIElementSpec specToMatch) {
[611]383        if (parentNode.children != null) {
384            for (TreeNode child : parentNode.children) {
[820]385                if (specToMatch.equals(child.guiElement.getSpecification())) {
386                    return child;
[611]387                }
388            }
389        }
[820]390        return null;
[611]391    }
392
393    /**
394     * <p>
[821]395     * Merges all similar nodes in the sub-tree of the GUI model defined by the subTreeRoot.
[611]396     * </p>
[821]397     * <p>
398     * The merging order is a bottom-up. This means, that we first call mergeSubTree recursively for
399     * the grand children of the subTreeRoot, before we merge subTreeRoot.
400     * </p>
401     * <p>
402     * The merging strategy is top-down. This means, that every time we merge two child nodes, we
403     * call mergeSubTree recursively for all children of the merged nodes in order to check if we
404     * can merge the children, too.
405     * </p>
[831]406     *
407     * @param subTreeRoot
408     *            root node of the sub-tree that is merged
[611]409     */
[821]410    private void mergeSubTree(TreeNode subTreeRoot) {
411        if (subTreeRoot.children == null || subTreeRoot.children.isEmpty()) {
[820]412            return;
[611]413        }
[820]414
[821]415        // lets first merge the grand children of the parentNode
416        for (TreeNode child : subTreeRoot.children) {
417            mergeSubTree(child);
[611]418        }
419
420        boolean performedMerge;
[820]421
[611]422        do {
423            performedMerge = false;
[821]424            for (int i = 0; !performedMerge && i < subTreeRoot.children.size(); i++) {
[820]425                IGUIElementSpec elemSpec1 =
[821]426                    subTreeRoot.children.get(i).guiElement.getSpecification();
427                for (int j = i + 1; !performedMerge && j < subTreeRoot.children.size(); j++) {
[820]428                    IGUIElementSpec elemSpec2 =
[821]429                        subTreeRoot.children.get(j).guiElement.getSpecification();
[820]430                    if (elemSpec1.getSimilarity(elemSpec2)) {
431                        TreeNode replacement =
[821]432                            mergeTreeNodes(subTreeRoot.children.get(i), subTreeRoot.children.get(j));
[820]433
[821]434                        subTreeRoot.children.set(i, replacement);
435                        subTreeRoot.children.remove(j);
[820]436                        performedMerge = true;
437                        i--;
438                        break;
[611]439                    }
440                }
441            }
442        }
443        while (performedMerge);
444    }
445
446    /**
447     * <p>
[820]448     * merges two nodes with each other. Merging means registering the GUI element objects with each
449     * other for equality checks. Further it add all children of both nodes to a new replacing node.
450     * Afterwards, all similar nodes of the replacement node are merged as well.
[611]451     * </p>
[820]452     *
[831]453     * @param treeNode1
454     *            the first of the two nodes to be merged
455     * @param treeNode2
456     *            the second of the two nodes to be merged
[746]457     * @return a tree node being the merge of the two provided nodes.
[611]458     */
459    private TreeNode mergeTreeNodes(TreeNode treeNode1, TreeNode treeNode2) {
460        // the following two lines are needed to preserve the references to the existing GUI
461        // elements. If two elements are the same, one should be deleted to make the elements
462        // singletons again. However, there may exist references to both objects. To preserve
463        // these, we simply register the equal GUI elements with each other so that an equals
464        // check can return true.
[820]465        treeNode1.guiElement.addEqualGUIElement(treeNode2.guiElement);
466        treeNode2.guiElement.addEqualGUIElement(treeNode1.guiElement);
[1116]467       
[820]468        // and now a replacement node that is the merge of treeNode1 and treeNode2 is created
469        TreeNode replacement = new TreeNode();
470        replacement.guiElement = treeNode1.guiElement;
[611]471        if (treeNode1.children != null) {
472            for (TreeNode child : treeNode1.children) {
[820]473                replacement.addChildNode(child);
[611]474            }
475        }
476        if (treeNode2.children != null) {
477            for (TreeNode child : treeNode2.children) {
[820]478                replacement.addChildNode(child);
[611]479            }
480        }
[820]481
[821]482        mergeSubTree(replacement);
[820]483
[611]484        replacement.guiElement.updateSpecification(treeNode2.guiElement.getSpecification());
[820]485
486        // finally, update the known nodes list
487        // if you don't do this getChildren will return wrong things and very bad things happen!
488        allNodes.remove(treeNode1);
489        allNodes.remove(treeNode2);
490        allNodes.add(replacement);
491
[611]492        return replacement;
493    }
494
495    /**
[746]496     * <p>
[820]497     * dumps a GUI element to the stream. A dump contains the toString() representation of the GUI
[1086]498     * element as well as a indented list of its children surrounded by braces. Therefore, not the
499     * GUI element itself but its tree node is provided to have an efficient access to its children
[746]500     * </p>
[831]501     *
502     * @param out
503     *            {@link PrintStream} where the guiElement is dumped to
[1086]504     * @param node
505     *            the guiElement's tree node of which the string representation is dumped
[831]506     * @param indent
507     *            indent string of the dumping
[643]508     */
[1086]509    private void dumpGUIElement(PrintStream out, TreeNode node, String indent) {
[643]510        out.print(indent);
[1086]511        out.print(node.guiElement);
[643]512
[1086]513        if ((node.children != null) && (node.children.size() > 0)) {
[643]514            out.println(" {");
515
[1086]516            for (TreeNode child : node.children) {
[643]517                dumpGUIElement(out, child, indent + "  ");
518            }
519
520            out.print(indent);
521            out.print("}");
522        }
523
524        out.println();
525    }
[846]526   
527    /**
528     * <p>
529     * Retrieves the TreeNode associated with a GUI element. Returns null if no such TreeNode is
530     * found.
531     * </p>
532     *
533     * @param element
534     *            the GUI element
535     * @return associated TreeNode; null if no such node exists
536     */
537    private TreeNode findNode(IGUIElement element) {
538        if (element == null) {
539            return null;
540        }
[643]541
[846]542        TreeNode result = null;
543        for (TreeNode node : allNodes) {
544            if (node.guiElement.equals(element)) {
545                if (result == null) {
546                    result = node;
547                }
548                else {
549                    Console
550                        .traceln(Level.SEVERE,
551                                 "Multiple nodes in the internal GUI model match the same GUI element. "
552                                     + "This should not be the case and the GUI model is probably invalid.");
553                }
554            }
555        }
556        return result;
557    }
558
[643]559    /**
[611]560     * <p>
[1086]561     * Used externally for tree traversal without providing direct access to the tree nodes
562     * </p>
563     *
564     * @version 1.0
565     * @author Patrick Harms, Steffen Herbold
566     */
567    public class Traverser {
568       
569        /**
570         * <p>
571         * the stack of nodes on which the traverser currently works
572         * </p>
573         */
574        private Stack<StackEntry> nodeStack = new Stack<StackEntry>();
575       
576        /**
577         * <p>
578         * initializes the traverser by adding the root node of the GUI model to the stack
579         * </p>
580         */
581        private Traverser() {
582            nodeStack.push(new StackEntry(root, 0));
583        }
584       
585        /**
586         * <p>
587         * returns the first child of the current GUI element. On the first call of this method on
588         * the traverser the first of the root GUI elements of the GUI model is returned. If the
589         * current GUI element does not have children, the method returns null. If the GUI model
590         * is empty, then a call to this method will return null. The returned GUI element is the
591         * next one the traverser points to.
592         * </p>
593         *
594         * @return as described.
595         */
596        public IGUIElement firstChild() {
597            return pushChild(0);
598        }
599       
600        /**
601         * <p>
602         * returns true, if the current GUI element has a first child, i.e. if the next call to the
603         * method {@link #firstChild()} would return a GUI element or null.
604         * </p>
605         *
606         * @return as described
607         */
608        public boolean hasFirstChild() {
609            return
610                (nodeStack.peek().treeNode.children != null) &&
611                (nodeStack.peek().treeNode.children.size() > 0);
612        }
613       
614        /**
615         * <p>
616         * returns the next sibling of the current GUI element. If there is no further sibling,
617         * null is returned. If the current GUI element is one of the root nodes, the next root
618         * node of the GUI model is returned. The returned GUI element is the next one the
619         * traverser points to.
620         * </p>
621         *
622         * @return as described
623         */
624        public IGUIElement nextSibling() {
625            int lastIndex = nodeStack.pop().index;
626           
627            IGUIElement retval = pushChild(lastIndex + 1);
628            if (retval == null) {
629                pushChild(lastIndex);
630            }
631           
632            return retval;
633        }
634       
635        /**
636         * <p>
637         * returns true, if the current GUI element has a further sibling, i.e. if a call to the
638         * method {@link #nextSibling()} will return a GUI element;
639         * </p>
640         *
641         * @return as described
642         */
643        public boolean hasNextSibling() {
[1088]644            boolean result = false;
645            if (nodeStack.size() > 1) {
646                StackEntry entry = nodeStack.pop();
647                result = nodeStack.peek().treeNode.children.size() > (entry.index + 1);
648                pushChild(entry.index);
649            }
650           
[1086]651            return result;
652        }
653       
654        /**
655         * <p>
656         * returns the parent GUI element of the current GUI element. If the current GUI element
657         * is a root node, null is returned. If there is no current GUI element yet as the method
658         * {@link #firstChild()} was not called yet, null is returned.
659         * </p>
660         *
661         * @return as described
662         */
663        public IGUIElement parent() {
664            IGUIElement retval = null;
665           
666            if (nodeStack.size() > 1) {
667                nodeStack.pop();
668                retval = nodeStack.peek().treeNode.guiElement;
669            }
670           
671            return retval;
672        }
673       
674        /**
675         * <p>
676         * internal method used for changing the state of the traverser. I.e. to switch to a
677         * specific child GUI element of the current one.
678         * </p>
679         */
680        private IGUIElement pushChild(int index) {
681            IGUIElement retVal = null;
682           
683            if ((nodeStack.peek().treeNode.children != null) &&
684                (nodeStack.peek().treeNode.children.size() > index))
685            {
686                nodeStack.push
687                    (new StackEntry(nodeStack.peek().treeNode.children.get(index), index));
688                retVal = nodeStack.peek().treeNode.guiElement;
689            }
690           
691            return retVal;
692        }
693       
694        /**
695         * <p>
[1088]696         * navigates the traverser to the given node in the GUI model
697         * </p>
698         */
699        private boolean navigateTo(TreeNode node) {
700            if (hasFirstChild()) {
701                IGUIElement childElement = firstChild();
702           
703                while (childElement != null) {
704                    if (childElement.equals(node.guiElement)) {
705                        return true;
706                    }
707                    else if (navigateTo(node)) {
708                        return true;
709                    }
710                    else {
711                        childElement = nextSibling();
712                    }
713                }
714           
715                parent();
716            }
717           
718            return false;
719        }
720
721        /**
722         * <p>
[1086]723         * internal class needed to fill the stack with nodes of the GUI models and their
724         * respective index in the children of the parent node.
725         * </p>
726         */
727        private class StackEntry {
728           
729            /** */
730            private TreeNode treeNode;
731           
732            /** */
733            private int index;
734           
735            /**
736             * <p>
737             * creates a new stack entry.
738             * </p>
739             */
740            private StackEntry(TreeNode treeNode, int index) {
741                this.treeNode = treeNode;
742                this.index = index;
743            }
744        }
745    }
746
747    /**
748     * <p>
[831]749     * Used internally for building up the tree of GUI elements.
[576]750     * </p>
[831]751     *
752     * @version 1.0
753     * @author Patrick Harms, Steffen Herbold
[576]754     */
[1181]755    private static class TreeNode implements Serializable {
[831]756
[1116]757        /**  */
758        private static final long serialVersionUID = 1L;
759
[831]760        /**
761         * <p>
762         * GUI element associated with the TreeNode.
763         * </p>
764         */
[576]765        private IGUIElement guiElement;
[820]766
[831]767        /**
768         * <p>
769         * Children of the TreeNode.
770         * </p>
771         */
[576]772        private List<TreeNode> children;
[820]773
[576]774        /**
775         * <p>
[831]776         * Adds a child to the current node while keeping all lists of nodes up to date
[576]777         * </p>
[831]778         *
779         * @param guiElement
780         *            GUI element that will be associated with the new child
781         * @return the added child
[576]782         */
[820]783        private TreeNode addChild(IGUIElement guiElement) {
784            if (children == null) {
[576]785                children = new ArrayList<TreeNode>();
786            }
[820]787
[576]788            TreeNode child = new TreeNode();
789            child.guiElement = guiElement;
790            children.add(child);
[820]791
[576]792            return child;
793        }
[709]794
[820]795        /**
796         *
797         * <p>
[821]798         * Adds a TreeNode as child to the current node. This way, the whole sub-tree is added.
[820]799         * </p>
[831]800         *
801         * @param node
802         *            child node that is added
803         * @return node that has been added
[820]804         */
805        private TreeNode addChildNode(TreeNode node) {
806            if (children == null) {
807                children = new ArrayList<TreeNode>();
808            }
809            children.add(node);
810            return node;
811        }
812
813        /*
814         * (non-Javadoc)
815         *
[709]816         * @see java.lang.Object#toString()
817         */
818        @Override
819        public String toString() {
820            return guiElement.toString();
821        }
[576]822    }
[545]823}
Note: See TracBrowser for help on using the repository browser.