Ignore:
Timestamp:
08/27/12 11:33:58 (12 years ago)
Author:
pharms
Message:
  • improved detection of similar nodes
  • implemented merging of subtrees in the case that the roots of both subtrees are identified as similar
File:
1 edited

Legend:

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

    r603 r611  
    4242        throws GUIModelException 
    4343    { 
     44        if ((guiElementPath == null) || (guiElementPath.size() <= 0)) { 
     45            throw new IllegalArgumentException 
     46                ("GUI element path must contain at least one element"); 
     47        } 
     48         
    4449        List<IGUIElementSpec> remainingPath = new LinkedList<IGUIElementSpec>(); 
    4550         
     
    103108    public List<IGUIElement> getRootElements() { 
    104109        List<IGUIElement> roots = new ArrayList<IGUIElement>(); 
    105         for (TreeNode rootChild : root.children) { 
    106             roots.add(rootChild.guiElement); 
    107         } 
     110         
     111        if (root.children != null) { 
     112            for (TreeNode rootChild : root.children) { 
     113                roots.add(rootChild.guiElement); 
     114            } 
     115        } 
     116         
    108117        return roots; 
    109118    } 
     
    126135    { 
    127136        IGUIElementSpec specToIntegrateElementFor = remainingPath.remove(0); 
    128          
    129         List<TreeNode> matchingChildren = new ArrayList<TreeNode>(); 
     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>(); 
    130205         
    131206        if (parentNode.children != null) { 
    132207            for (TreeNode child : parentNode.children) { 
    133                 if (specToIntegrateElementFor.getSimilarity(child.guiElement.getSpecification())) { 
    134                     matchingChildren.add(child); 
    135                 } 
    136             } 
    137         } 
    138          
    139         // if we get here, the corresponding path does not exist yet. So create it 
    140         if (matchingChildren.size() == 0) { 
    141             IGUIElement newElement = guiElementFactory.instantiateGUIElement 
    142                 (specToIntegrateElementFor, parentNode.guiElement); 
    143              
    144             matchingChildren.add(parentNode.addChild(newElement)); 
    145         } 
    146         else if (matchingChildren.size() > 1) { 
    147             throw new GUIModelException 
    148               ("several children of gui element " + parentNode.guiElement + 
    149                " match the specification " + specToIntegrateElementFor + " at the same level. " + 
    150                "Can not decide which is the right one."); 
    151         } 
    152          
    153         if (remainingPath.size() > 0) { 
    154             matchingChildren.get(0).guiElement.updateSpecification(specToIntegrateElementFor); 
    155             return integratePath(matchingChildren.get(0), remainingPath, guiElementFactory); 
     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            } 
    156240        } 
    157241        else { 
    158             return matchingChildren.get(0).guiElement; 
    159         } 
     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; 
    160332    } 
    161333 
Note: See TracChangeset for help on using the changeset viewer.