Ignore:
Timestamp:
07/25/13 14:45:00 (11 years ago)
Author:
pharms
Message:
  • added support for grouping GUI elements
  • performance improvements
  • extended merge support for including non-recursive merges
  • added switch for validation
Location:
trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel
Files:
1 added
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIModel.java

    r1181 r1260  
    2020import java.io.UnsupportedEncodingException; 
    2121import java.util.ArrayList; 
     22import java.util.HashMap; 
    2223import java.util.LinkedList; 
    2324import java.util.List; 
     25import java.util.Map; 
    2426import java.util.Stack; 
    2527import java.util.logging.Level; 
     
    5254    /** 
    5355     * <p> 
    54      * A list with all nodes currently known 
    55      * </p> 
    56      */ 
    57     private List<TreeNode> allNodes = new ArrayList<TreeNode>(); 
     56     * A map with all nodes currently known 
     57     * </p> 
     58     */ 
     59    private Map<IGUIElement, TreeNode> allNodes = new HashMap<IGUIElement, TreeNode>(); 
     60     
     61    /** 
     62     * <p> 
     63     * true, if internal validation is switched on, false else 
     64     * </p> 
     65     */ 
     66    private boolean validate = false; 
     67 
     68    /** 
     69     * <p> 
     70     * Default constructor to create a GUI model without internal validation 
     71     * </p> 
     72     * 
     73     */ 
     74    public GUIModel() { 
     75        this(false); 
     76    } 
     77 
     78    /** 
     79     * <p> 
     80     * creates a GUI model, that internally validates itself by checking on access to nodes, 
     81     * if several GUI elements pretend to be equal or if several distinct GUI elements have the 
     82     * same child. 
     83     * </p> 
     84     * 
     85     * @param validate 
     86     *            true if internal validation shall be switched on (bad performance), false else 
     87     * 
     88     */ 
     89    public GUIModel(boolean validate) { 
     90        this.validate = validate; 
     91    } 
    5892 
    5993    /** 
     
    107141        } 
    108142 
    109         List<IGUIElementSpec> remainingPath = new LinkedList<IGUIElementSpec>(); 
    110  
    111         for (IGUIElementSpec spec : guiElementPath) { 
    112             remainingPath.add(spec); 
    113         } 
     143        List<IGUIElementSpec> remainingPath = new LinkedList<IGUIElementSpec>(guiElementPath); 
    114144 
    115145        return integratePath(root, remainingPath, guiElementFactory); 
     
    128158     */ 
    129159    public List<IGUIElement> getChildren(IGUIElement guiElement) { 
     160        TreeNode node = findNode(guiElement); 
     161         
    130162        List<IGUIElement> result = null; 
    131         for (TreeNode node : allNodes) { 
    132             if (node.guiElement.equals(guiElement)) { 
    133                 if (result == null) { 
    134                     result = new ArrayList<IGUIElement>(); 
    135  
    136                     if (node.children != null) { 
    137                         for (TreeNode child : node.children) { 
    138                             result.add(child.guiElement); 
    139                         } 
    140                     } 
     163        if (node != null) { 
     164            result = new LinkedList<IGUIElement>(); 
     165            if (node.children != null) { 
     166                for (TreeNode child : node.children) { 
     167                    result.add(child.guiElement); 
    141168                } 
    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                 } 
    148             } 
    149         } 
    150  
     169            } 
     170        } 
     171  
    151172        return result; 
    152173    } 
     
    166187        IGUIElement parent = null; 
    167188 
    168         for (TreeNode node : allNodes) { 
    169             if (node.children != null) { 
    170                 for (TreeNode child : node.children) { 
     189        for (Map.Entry<IGUIElement, TreeNode> entry : allNodes.entrySet()) { 
     190            if (entry.getValue().children != null) { 
     191                for (TreeNode child : entry.getValue().children) { 
    171192                    if (child.guiElement.equals(guiElement)) { 
    172                         if (parent != null) { 
    173                             parent = node.guiElement; 
     193                        if (parent == null) { 
     194                            parent = entry.getKey(); 
     195                            if (!validate) { 
     196                                break; 
     197                            } 
    174198                        } 
    175199                        else { 
     
    269293    /** 
    270294     * <p> 
     295     * This method groups the provided GUI elements under a common parent GUI element. The current 
     296     * parent GUI element of the GUI elements to group must be the same. If the GUI elements to 
     297     * be grouped are the whole list of children of the same parent, nothing is changed.  
     298     * </p> 
     299     *  
     300     * @param guiElements the list of GUI elements to be grouped 
     301     * @param groupName   the name of the GUI element group to be created 
     302     *  
     303     * @return the GUI element representing the group, or null, if the provided list of GUI elements 
     304     *         is empty 
     305     *  
     306     * @throws IllegalArgumentException 
     307     *             if not all GUI elements to be merged share the same parent, if one of the 
     308     *             parameters is null, or if one of the provided GUI elements does not belong to 
     309     *             the model 
     310     */ 
     311    public IGUIElement groupGUIElements(List<IGUIElement> guiElements, String groupName) 
     312        throws IllegalArgumentException 
     313    { 
     314        if ((guiElements == null) || (groupName == null)) { 
     315            throw new IllegalArgumentException("parameters must not be null"); 
     316        } 
     317         
     318        if (guiElements.size() <= 0) { 
     319            // do nothing 
     320            return null; 
     321        } 
     322         
     323        TreeNode parent = findNode(guiElements.get(0).getParent()); 
     324         
     325        List<TreeNode> nodesToGroup = new LinkedList<TreeNode>(); 
     326         
     327        for (IGUIElement element : guiElements) { 
     328            if (!(element instanceof AbstractDefaultGUIElement)) { 
     329                throw new IllegalArgumentException 
     330                    ("can only group nodes of type AbstractDefaultGUIElement"); 
     331            } 
     332             
     333            TreeNode node = findNode(element); 
     334            if (node == null) { 
     335                throw new IllegalArgumentException 
     336                    ("GUI element " + element + " is not part of the model"); 
     337            } 
     338             
     339            if (!nodesToGroup.contains(node)) { 
     340                nodesToGroup.add(node); 
     341            } 
     342             
     343            TreeNode parentNode = findNode(element.getParent()); 
     344             
     345            if (!parent.equals(parentNode)) { 
     346                throw new IllegalArgumentException("GUI elements do not share the same parent: " + 
     347                                                   parent + " <> " + parentNode); 
     348            } 
     349        } 
     350         
     351        TreeNode replacement = new TreeNode(); 
     352        replacement.guiElement = new GUIElementGroup(groupName, parent.guiElement); 
     353         
     354        for (TreeNode child : nodesToGroup) { 
     355            ((GUIElementGroup) replacement.guiElement).addToGroup(child.guiElement); 
     356            replacement.addChildNode(child); 
     357            ((AbstractDefaultGUIElement) child.guiElement).setParent(replacement.guiElement); 
     358            parent.children.remove(child); 
     359        } 
     360 
     361        parent.children.add(replacement); 
     362 
     363        // finally, update the known nodes list 
     364        // if you don't do this getChildren will return wrong things and very bad things happen! 
     365        allNodes.put(replacement.guiElement, replacement); 
     366         
     367        return replacement.guiElement; 
     368    } 
     369     
     370    /** 
     371     * <p> 
    271372     * By calling this method, the GUIModel is traversed and similar nodes are merged. 
    272373     * </p> 
     
    280381     * <p> 
    281382     * Merges the tree nodes of two GUI elements. The GUI elements need to have the same parent. 
     383     * They are merged recursively, i.e. also their children are merged.  
    282384     * </p> 
    283385     *  
     
    286388     * @param guiElement2 
    287389     *            the second merge GUI element 
     390     *             
     391     * @return the result of the merge 
     392     *             
    288393     * @throws IllegalArgumentException 
    289394     *             thrown if the two GUI elements do not have the same parent 
    290395     */ 
    291     public void mergeGUIElements(IGUIElement guiElement1, IGUIElement guiElement2) 
     396    public IGUIElement mergeGUIElements(IGUIElement guiElement1, IGUIElement guiElement2) 
     397        throws IllegalArgumentException 
     398    { 
     399        return mergeGUIElements(guiElement1, guiElement2, true); 
     400    } 
     401     
     402    /** 
     403     * <p> 
     404     * Merges the tree nodes of two GUI elements. The GUI elements need to have the same parent. 
     405     * If the <code>recursively</code> parameter is set to true, the children of the GUI elements 
     406     * are merged, as well, as long as they are similar. If the parameter is false, the children 
     407     * are not merged. In this case the resulting GUI element has all children of both merged GUI 
     408     * elements. 
     409     * </p> 
     410     *  
     411     * @param guiElement1 
     412     *            the first merge GUI element 
     413     * @param guiElement2 
     414     *            the second merge GUI element 
     415     * @param recursively 
     416     *            if true, the merge is done also for similar children, if false, not. 
     417     *             
     418     * @return the result of the merge 
     419     *             
     420     * @throws IllegalArgumentException 
     421     *             thrown if the two GUI elements do not have the same parent 
     422     */ 
     423    public IGUIElement mergeGUIElements(IGUIElement guiElement1, 
     424                                        IGUIElement guiElement2, 
     425                                        boolean     recursively) 
    292426        throws IllegalArgumentException 
    293427    { 
     
    310444        } 
    311445 
    312         TreeNode replacement = mergeTreeNodes(node1, node2); 
     446        TreeNode replacement = mergeTreeNodes(node1, node2, recursively); 
    313447 
    314448        if (parent != null) { 
     
    321455        } 
    322456 
     457        return replacement.guiElement; 
    323458    } 
    324459 
     
    356491 
    357492            child = parentNode.addChild(newElement); 
    358             allNodes.add(child); 
     493            allNodes.put(child.guiElement, child); 
    359494        } 
    360495 
     
    429564                        subTreeRoot.children.get(j).guiElement.getSpecification(); 
    430565                    if (elemSpec1.getSimilarity(elemSpec2)) { 
    431                         TreeNode replacement = 
    432                             mergeTreeNodes(subTreeRoot.children.get(i), subTreeRoot.children.get(j)); 
     566                        TreeNode replacement = mergeTreeNodes 
     567                            (subTreeRoot.children.get(i), subTreeRoot.children.get(j), true); 
    433568 
    434569                        subTreeRoot.children.set(i, replacement); 
     
    447582     * <p> 
    448583     * 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. 
     584     * other for equality checks. Further it adds all children of both nodes to a new replacing 
     585     * node. Afterwards, all similar nodes of the replacement node are merged as well as long 
     586     * the recursive parameter is set to true. 
    451587     * </p> 
    452588     *  
     
    455591     * @param treeNode2 
    456592     *            the second of the two nodes to be merged 
     593     * @param recursively 
     594     *            if true, the merging also merges child nodes 
     595     *             
    457596     * @return a tree node being the merge of the two provided nodes. 
    458597     */ 
    459     private TreeNode mergeTreeNodes(TreeNode treeNode1, TreeNode treeNode2) { 
     598    private TreeNode mergeTreeNodes(TreeNode treeNode1, TreeNode treeNode2, boolean recursively) { 
     599        // and now a replacement node that is the merge of treeNode1 and treeNode2 is created 
     600        TreeNode replacement = new TreeNode(); 
     601        replacement.guiElement = treeNode1.guiElement; 
     602        if (treeNode1.children != null) { 
     603            for (TreeNode child : treeNode1.children) { 
     604                replacement.addChildNode(child); 
     605            } 
     606        } 
     607        if (treeNode2.children != null) { 
     608            for (TreeNode child : treeNode2.children) { 
     609                replacement.addChildNode(child); 
     610            } 
     611        } 
     612 
     613        if (recursively) { 
     614            mergeSubTree(replacement); 
     615        } 
     616 
     617        replacement.guiElement.updateSpecification(treeNode2.guiElement.getSpecification()); 
     618 
     619        // finally, update the known nodes list 
     620        // if you don't do this getChildren will return wrong things and very bad things happen! 
     621        allNodes.remove(treeNode1.guiElement); 
     622        allNodes.remove(treeNode2.guiElement); 
     623 
    460624        // the following two lines are needed to preserve the references to the existing GUI 
    461625        // elements. If two elements are the same, one should be deleted to make the elements 
     
    466630        treeNode2.guiElement.addEqualGUIElement(treeNode1.guiElement); 
    467631         
    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; 
    471         if (treeNode1.children != null) { 
    472             for (TreeNode child : treeNode1.children) { 
    473                 replacement.addChildNode(child); 
    474             } 
    475         } 
    476         if (treeNode2.children != null) { 
    477             for (TreeNode child : treeNode2.children) { 
    478                 replacement.addChildNode(child); 
    479             } 
    480         } 
    481  
    482         mergeSubTree(replacement); 
    483  
    484         replacement.guiElement.updateSpecification(treeNode2.guiElement.getSpecification()); 
    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); 
     632        allNodes.put(replacement.guiElement, replacement); 
    491633 
    492634        return replacement; 
     
    541683 
    542684        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."); 
     685         
     686        if (!validate) { 
     687            result = allNodes.get(element); 
     688        } 
     689        else { 
     690            for (Map.Entry<IGUIElement, TreeNode> entry : allNodes.entrySet()) { 
     691                if (entry.getKey().equals(element)) { 
     692                    if (result == null) { 
     693                        result = entry.getValue(); 
     694                    } 
     695                    else { 
     696                        Console.traceln(Level.SEVERE, "Multiple nodes in the internal GUI model " + 
     697                                        "match the same GUI element. This should not be the case " + 
     698                                        "and the GUI model is probably invalid."); 
     699                    } 
    553700                } 
    554701            } 
     
    820967            return guiElement.toString(); 
    821968        } 
     969 
    822970    } 
    823971} 
Note: See TracChangeset for help on using the changeset viewer.