[1918] | 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. |
---|
| 14 | |
---|
| 15 | package de.ugoe.cs.autoquest.usability; |
---|
| 16 | |
---|
| 17 | import java.util.Collection; |
---|
| 18 | import java.util.HashMap; |
---|
| 19 | import java.util.Iterator; |
---|
| 20 | import java.util.LinkedList; |
---|
| 21 | import java.util.List; |
---|
| 22 | import java.util.ListIterator; |
---|
| 23 | import java.util.Map; |
---|
| 24 | |
---|
| 25 | import de.ugoe.cs.autoquest.eventcore.guimodel.GUIElementGroup; |
---|
| 26 | import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement; |
---|
| 27 | |
---|
| 28 | /** |
---|
| 29 | * <p> |
---|
| 30 | * TODO comment |
---|
| 31 | * </p> |
---|
| 32 | * |
---|
| 33 | * @author Patrick Harms |
---|
| 34 | */ |
---|
| 35 | class RuleUtils { |
---|
| 36 | |
---|
| 37 | /** |
---|
| 38 | * |
---|
| 39 | */ |
---|
| 40 | static Map<IGUIElement, List<IGUIElement>> getGroups(Collection<IGUIElement> guiElements, |
---|
| 41 | int maxDistToCommonParent) |
---|
| 42 | { |
---|
| 43 | Map<IGUIElement, List<IGUIElement>> groups = new HashMap<>(); |
---|
| 44 | List<IGUIElement> guiElementsToGroup = new LinkedList<>(guiElements); |
---|
| 45 | IGUIElement parentToGroup; |
---|
| 46 | |
---|
| 47 | do { |
---|
| 48 | List<LinkedList<IGUIElement>> sortedPaths = new LinkedList<>(); |
---|
| 49 | List<IGUIElement> commonParents = new LinkedList<>(); |
---|
| 50 | |
---|
| 51 | createSortedPaths |
---|
| 52 | (guiElementsToGroup, sortedPaths, commonParents, maxDistToCommonParent); |
---|
| 53 | |
---|
| 54 | // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION |
---|
| 55 | // Iterator<LinkedList<IGUIElement>> sortedPathsIt1 = sortedPaths.iterator(); |
---|
| 56 | // Iterator<IGUIElement> commonParentIt1 = commonParents.iterator(); |
---|
| 57 | // |
---|
| 58 | // while (commonParentIt1.hasNext()) { |
---|
| 59 | // IGUIElement currentParent = commonParentIt1.next(); |
---|
| 60 | // IGUIElement sortedPath = sortedPathsIt1.next().getLast(); |
---|
| 61 | // |
---|
| 62 | // System.out.println(toPathString(sortedPath)); |
---|
| 63 | // System.out.println(toPathString(currentParent) + "###########"); |
---|
| 64 | // } |
---|
| 65 | // |
---|
| 66 | // System.out.println(); |
---|
| 67 | // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION |
---|
| 68 | |
---|
| 69 | parentToGroup = getParentToGroup(commonParents); |
---|
| 70 | |
---|
| 71 | if (parentToGroup != null) { |
---|
| 72 | Iterator<LinkedList<IGUIElement>> sortedPathsIt = sortedPaths.iterator(); |
---|
| 73 | ListIterator<IGUIElement> commonParentIt = commonParents.listIterator(); |
---|
| 74 | |
---|
| 75 | while (commonParentIt.hasNext()) { |
---|
| 76 | if (!parentToGroup.equals(commonParentIt.next())) { |
---|
| 77 | sortedPathsIt.next(); |
---|
| 78 | } |
---|
| 79 | else { |
---|
| 80 | break; |
---|
| 81 | } |
---|
| 82 | } |
---|
| 83 | |
---|
| 84 | List<IGUIElement> groupedGUIElements = new LinkedList<>(); |
---|
| 85 | |
---|
| 86 | // go one backward to ensure, that next will return the first occurrence |
---|
| 87 | // of the parent to create the group for. |
---|
| 88 | commonParentIt.previous(); |
---|
| 89 | |
---|
| 90 | do { |
---|
| 91 | IGUIElement guiElementToGroup = sortedPathsIt.next().getLast(); |
---|
| 92 | groupedGUIElements.add(guiElementToGroup); |
---|
| 93 | guiElementsToGroup.remove(guiElementToGroup); |
---|
| 94 | |
---|
| 95 | } |
---|
| 96 | while (parentToGroup.equals(commonParentIt.next())); |
---|
| 97 | |
---|
| 98 | groups.put(parentToGroup, groupedGUIElements); |
---|
| 99 | |
---|
| 100 | // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TEST IMPLEMENTATION |
---|
| 101 | // System.out.println("group for"); |
---|
| 102 | // System.out.println(toPathString(parentToGroup)); |
---|
| 103 | // |
---|
| 104 | // for (IGUIElement element : groupedGUIElements) { |
---|
| 105 | // System.out.println(toPathString(element)); |
---|
| 106 | // } |
---|
| 107 | // |
---|
| 108 | // System.out.println(); |
---|
| 109 | // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TEST IMPLEMENTATION |
---|
| 110 | } |
---|
| 111 | } |
---|
| 112 | while (parentToGroup != null); |
---|
| 113 | |
---|
| 114 | return groups; |
---|
| 115 | } |
---|
| 116 | |
---|
| 117 | /** |
---|
| 118 | * |
---|
| 119 | */ |
---|
| 120 | static String toPathString(IGUIElement element) { |
---|
| 121 | IGUIElement parent = element; |
---|
| 122 | String result = ""; |
---|
| 123 | |
---|
| 124 | while (parent != null) { |
---|
| 125 | if (!(parent instanceof GUIElementGroup)) { |
---|
| 126 | result = parent.toString() + "/" + result; |
---|
| 127 | } |
---|
| 128 | else { |
---|
| 129 | result = parent.toString().hashCode() + "/" + result; |
---|
| 130 | } |
---|
| 131 | |
---|
| 132 | parent = parent.getParent(); |
---|
| 133 | } |
---|
| 134 | |
---|
| 135 | return result.toString(); |
---|
| 136 | } |
---|
| 137 | |
---|
| 138 | /** |
---|
| 139 | * |
---|
| 140 | */ |
---|
| 141 | private static void createSortedPaths(List<IGUIElement> guiElements, |
---|
| 142 | List<LinkedList<IGUIElement>> sortedPaths, |
---|
| 143 | List<IGUIElement> commonParents, |
---|
| 144 | int maxDistToCommonParent) |
---|
| 145 | { |
---|
| 146 | for (IGUIElement guiElement : guiElements) { |
---|
| 147 | // create the path |
---|
| 148 | LinkedList<IGUIElement> path = new LinkedList<>(); |
---|
| 149 | IGUIElement parent = guiElement; |
---|
| 150 | while (parent != null) { |
---|
| 151 | if (!(parent instanceof GUIElementGroup)) { |
---|
| 152 | path.addFirst(parent); |
---|
| 153 | } |
---|
| 154 | |
---|
| 155 | parent = parent.getParent(); |
---|
| 156 | } |
---|
| 157 | |
---|
| 158 | // sort it into the list of paths |
---|
| 159 | int maxEquality = 0; |
---|
| 160 | int maxEqualityPos = 0; |
---|
| 161 | int pos = 0; |
---|
| 162 | |
---|
| 163 | for (List<IGUIElement> candidate : sortedPaths) { |
---|
| 164 | int equality = 0; |
---|
| 165 | while ((equality < candidate.size()) && (equality < path.size()) && |
---|
| 166 | (candidate.get(equality).equals(path.get(equality)))) |
---|
| 167 | { |
---|
| 168 | equality++; |
---|
| 169 | } |
---|
| 170 | |
---|
| 171 | if (equality > maxEquality) { |
---|
| 172 | maxEquality = equality; |
---|
| 173 | maxEqualityPos = pos; |
---|
| 174 | } |
---|
| 175 | |
---|
| 176 | pos++; |
---|
| 177 | } |
---|
| 178 | |
---|
| 179 | sortedPaths.add(maxEqualityPos, path); |
---|
| 180 | |
---|
| 181 | if ((maxEquality > 0) && ((path.size() - maxEquality) < maxDistToCommonParent)) { |
---|
| 182 | commonParents.add(maxEqualityPos, path.get(maxEquality - 1)); |
---|
| 183 | } |
---|
| 184 | else { |
---|
| 185 | commonParents.add(maxEqualityPos, null); |
---|
| 186 | } |
---|
| 187 | } |
---|
| 188 | } |
---|
| 189 | |
---|
| 190 | /** |
---|
| 191 | * |
---|
| 192 | */ |
---|
| 193 | private static IGUIElement getParentToGroup(List<IGUIElement> commonParents) { |
---|
| 194 | Map<IGUIElement, Integer> occurrenceCounts = new HashMap<>(); |
---|
| 195 | Map<IGUIElement, Integer> depths = new HashMap<>(); |
---|
| 196 | |
---|
| 197 | // get the required information about path lengths and occurrence counts |
---|
| 198 | for (IGUIElement commonParent : commonParents) { |
---|
| 199 | Integer occurrenceCount = occurrenceCounts.get(commonParent); |
---|
| 200 | |
---|
| 201 | if (occurrenceCount != null) { |
---|
| 202 | occurrenceCounts.put(commonParent, occurrenceCount + 1); |
---|
| 203 | } |
---|
| 204 | else { |
---|
| 205 | occurrenceCounts.put(commonParent, 1); |
---|
| 206 | } |
---|
| 207 | |
---|
| 208 | if (!depths.containsKey(commonParent)) { |
---|
| 209 | int depth = 0; |
---|
| 210 | IGUIElement parent = commonParent; |
---|
| 211 | |
---|
| 212 | while (parent != null) { |
---|
| 213 | depth++; |
---|
| 214 | parent = parent.getParent(); |
---|
| 215 | } |
---|
| 216 | |
---|
| 217 | depths.put(commonParent, depth); |
---|
| 218 | } |
---|
| 219 | } |
---|
| 220 | |
---|
| 221 | IGUIElement elementToGroup = null; |
---|
| 222 | |
---|
| 223 | // get the GUI element being the parent most often |
---|
| 224 | for (IGUIElement commonParent : commonParents) { |
---|
| 225 | if (elementToGroup == null) { |
---|
| 226 | elementToGroup = commonParent; |
---|
| 227 | } |
---|
| 228 | else if ((commonParent != null) && |
---|
| 229 | (!elementToGroup.equals(commonParent))) |
---|
| 230 | { |
---|
| 231 | int occurrenceCountCandidate = occurrenceCounts.get(commonParent); |
---|
| 232 | int depthCandidate = depths.get(commonParent); |
---|
| 233 | |
---|
| 234 | int occurrenceCountElement = occurrenceCounts.get(elementToGroup); |
---|
| 235 | int depthElement = depths.get(elementToGroup); |
---|
| 236 | |
---|
| 237 | if ((depthCandidate > depthElement) || |
---|
| 238 | ((depthCandidate == depthElement) && |
---|
| 239 | (occurrenceCountCandidate > occurrenceCountElement))) |
---|
| 240 | { |
---|
| 241 | elementToGroup = commonParent; |
---|
| 242 | } |
---|
| 243 | else if ((occurrenceCountCandidate == occurrenceCountElement) && |
---|
| 244 | (depthCandidate == depthElement)) |
---|
| 245 | { |
---|
| 246 | // in this situation, the order is irrelevant. The GUI elements for which |
---|
| 247 | // both paths were identified as parents are completely different. |
---|
| 248 | // Otherwise, they would have the same parent. But as they are that |
---|
| 249 | // different, they will not occur subsequently in the ordered list |
---|
| 250 | // of GUI elements to group. Hence, we just reuse the one the is currently |
---|
| 251 | // identified as the one to group next. |
---|
| 252 | } |
---|
| 253 | } |
---|
| 254 | } |
---|
| 255 | |
---|
| 256 | return elementToGroup; |
---|
| 257 | } |
---|
| 258 | |
---|
| 259 | /** |
---|
| 260 | * |
---|
| 261 | */ |
---|
| 262 | private RuleUtils() { |
---|
| 263 | // prevent instantiation |
---|
| 264 | } |
---|
| 265 | } |
---|