Changeset 820
Legend:
- Unmodified
- Added
- Removed
-
trunk/quest-core-events/src/main/java/de/ugoe/cs/quest/eventcore/guimodel/GUIModel.java
r747 r820 1 1 2 package de.ugoe.cs.quest.eventcore.guimodel; 2 3 … … 7 8 import java.util.LinkedList; 8 9 import java.util.List; 9 import java.util.logging.Level;10 11 import de.ugoe.cs.util.console.Console;12 10 13 11 /** … … 16 14 * platform independent. It may have several root nodes, as some GUIs are made up of several Frames 17 15 * being independent from each other. The GUI model is filled using the 18 * {@link #integratePath(List, IGUIElementFactory)} method. 16 * {@link #integratePath(List, IGUIElementFactory)} method. 19 17 * </p> 20 18 * 19 * TODO: getChildren and getParent should be rewritten to at least throw warnings, in case there are 20 * multiple nodes in allNodes that match the current GUI element. 21 * 21 22 * @version $Revision: $ $Date: 14.08.2012$ 22 * @author 2012, last modified by $Author: pharms$23 * @author Patrick Harms, Steffen Herbold 23 24 */ 24 25 public class GUIModel { 25 26 26 27 /** 27 28 * <p> … … 30 31 */ 31 32 private TreeNode root = new TreeNode(); 32 33 33 34 /** 34 35 * <p> … … 44 45 * a leaf node. Such a path can be added to the tree. The method checks, if any of the GUI 45 46 * elements denoted by the path already exists. If so, it reuses it. It may therefore also 46 * return an existing GUI element being the leaf node of the provided path. If a GUI element 47 * ofthe path does not exist yet, it creates a new one using the provided GUI element factory.48 * </p> 49 * <p> 50 * If a GUI element specification describes an existing GUI element or not is determined 51 * through comparing the GUI element specifications of the existing GUI elements with the52 * ones providedin the path. The comparison is done using the53 * {@link IGUIElementSpec#getSimilarity(IGUIElementSpec)} method. The comparison is only done 54 * on the correct levels. I.e. the currently known root elements of the tree are only compared55 * t o the first element in the path. If the correct one is found or created, its children are47 * return an existing GUI element being the leaf node of the provided path. If a GUI element of 48 * the path does not exist yet, it creates a new one using the provided GUI element factory. 49 * </p> 50 * <p> 51 * If a GUI element specification describes an existing GUI element or not is determined through 52 * comparing the GUI element specifications of the existing GUI elements with the ones provided 53 * in the path. The comparison is done using the 54 * {@link IGUIElementSpec#getSimilarity(IGUIElementSpec)} method. The comparison is only done on 55 * the correct levels. I.e. the currently known root elements of the tree are only compared to 56 * the first element in the path. If the correct one is found or created, its children are 56 57 * compared only to the second specification in the path, and so on. 57 58 * </p> 58 59 * <p> 59 60 * The returned GUI elements are singletons. I.e. it is tried to return always the identical 60 * object for the same denoted element. However, while creating the GUI model, the similarity 61 * of GUI elements may change. Therefore, the method might determine, that two formerly62 * different nodes are now similar. (This may happen, e.g. if GUI elements do not have initial63 * names which are set afterwards. Therefore, first they are handled differently and later64 * they can be identified as being the same.) In such a case, there are already several GUI65 * element objects instantiated for the same GUI element. The singleton paradigm gets broken.66 * Therefore, such GUI element objects are registered with each other, so that their equal67 * method can determine equality again correctly, although the objects are no singletons68 * anymore.69 * </p>70 * 71 * @param guiElementPaththe path to integrate into the model72 * @param guiElementFactory the GUI element factory to be used for instantiating GUI element73 * 74 * 61 * object for the same denoted element. However, while creating the GUI model, the similarity of 62 * GUI elements may change. Therefore, the method might determine, that two formerly different 63 * nodes are now similar. (This may happen, e.g. if GUI elements do not have initial names which 64 * are set afterwards. Therefore, first they are handled differently and later they can be 65 * identified as being the same.) In such a case, there are already several GUI element objects 66 * instantiated for the same GUI element. The singleton paradigm gets broken. Therefore, such 67 * GUI element objects are registered with each other, so that their equal method can determine 68 * equality again correctly, although the objects are no singletons anymore. 69 * </p> 70 * 71 * @param guiElementPath 72 * the path to integrate into the model 73 * @param guiElementFactory 74 * the GUI element factory to be used for instantiating GUI element objects 75 * 75 76 * @return The GUI element object representing the GUI element denoted by the provided path 76 77 * 77 * @throws GUIModelException thrown in cases such as the GUI element object could not be 78 * instantiated 79 * @throws IllegalArgumentException if the provided path is invalid. 78 * @throws GUIModelException 79 * thrown in cases such as the GUI element object could not be instantiated 80 * @throws IllegalArgumentException 81 * if the provided path is invalid. 80 82 */ 81 83 public IGUIElement integratePath(List<? extends IGUIElementSpec> guiElementPath, 82 IGUIElementFactory 84 IGUIElementFactory guiElementFactory) 83 85 throws GUIModelException, IllegalArgumentException 84 86 { 85 87 if ((guiElementPath == null) || (guiElementPath.size() <= 0)) { 86 throw new IllegalArgumentException 87 ("GUI element path must contain at least one element"); 88 } 89 88 throw new IllegalArgumentException("GUI element path must contain at least one element"); 89 } 90 90 91 List<IGUIElementSpec> remainingPath = new LinkedList<IGUIElementSpec>(); 91 92 for (IGUIElementSpec spec : guiElementPath) 93 { 92 93 for (IGUIElementSpec spec : guiElementPath) { 94 94 remainingPath.add(spec); 95 95 } 96 96 97 97 return integratePath(root, remainingPath, guiElementFactory); 98 98 } … … 100 100 /** 101 101 * <p> 102 * Returns all children of the provided GUI element or null, if it does not have any or the 103 * node is unknown. 104 * </p> 105 * 106 * @param guiElement the GUI element of which the children shall be returned 102 * Returns all children of the provided GUI element or null, if it does not have any or the node 103 * is unknown. 104 * </p> 105 * 106 * @param guiElement 107 * the GUI element of which the children shall be returned 107 108 * 108 109 * @return As described … … 112 113 if (node.guiElement.equals(guiElement)) { 113 114 List<IGUIElement> result = new ArrayList<IGUIElement>(); 114 115 115 116 if (node.children != null) { 116 117 for (TreeNode child : node.children) { 117 result.add(child.guiElement);118 result.add(child.guiElement); 118 119 } 119 120 } 120 121 121 122 return result; 122 123 } 123 124 } 124 125 125 126 return null; 126 127 } … … 131 132 * parent (i.e. if it is a root node) or if the node is unknown. 132 133 * </p> 133 * 134 * @param guiElement the GUI element of which the parent shall be returned 134 * 135 * @param guiElement 136 * the GUI element of which the parent shall be returned 135 137 * 136 138 * @return As described … … 144 146 } 145 147 } 146 148 147 149 return null; 148 150 } … … 152 154 * Returns all root GUI elements of the model or an empty list, if the model is empty 153 155 * </p> 154 * 156 * 155 157 * @return As described 156 158 */ 157 159 public List<IGUIElement> getRootElements() { 158 160 List<IGUIElement> roots = new ArrayList<IGUIElement>(); 159 161 160 162 if (root.children != null) { 161 163 for (TreeNode rootChild : root.children) { … … 163 165 } 164 166 } 165 167 166 168 return roots; 167 169 } … … 173 175 * </p> 174 176 * 175 * @param out The stream to dump the textual representation of the model to 176 * @param encoding The encoding to be used while dumping 177 * @param out 178 * The stream to dump the textual representation of the model to 179 * @param encoding 180 * The encoding to be used while dumping 177 181 */ 178 182 public void dump(OutputStream out, String encoding) { 179 183 PrintStream stream; 180 184 181 185 if (out instanceof PrintStream) { 182 186 stream = (PrintStream) out; … … 191 195 } 192 196 } 193 197 194 198 for (IGUIElement root : getRootElements()) { 195 199 dumpGUIElement(stream, root, ""); … … 199 203 /** 200 204 * <p> 201 * internally integrates a path as the children of the provided parent node. This method 202 * is recursive and calls itself, for the child of the parent node, that matches the first 203 * element in the remaining path. 204 * </p> 205 * 206 * @param parentNode the parent node to add children for 207 * @param guiElementPath the path of children to be created starting with the parent node 208 * @param guiElementFactory the GUI element factory to be used for instantiating GUI element 209 * objects 210 * 205 * TODO: comment 206 * </p> 207 * 208 */ 209 public void condenseModel() { 210 mergeSimilarChildren(root); 211 } 212 213 /** 214 * <p> 215 * internally integrates a path as the children of the provided parent node. This method is 216 * recursive and calls itself, for the child of the parent node, that matches the first element 217 * in the remaining path. 218 * </p> 219 * 220 * @param parentNode 221 * the parent node to add children for 222 * @param guiElementPath 223 * the path of children to be created starting with the parent node 224 * @param guiElementFactory 225 * the GUI element factory to be used for instantiating GUI element objects 226 * 211 227 * @return The GUI element object representing the GUI element denoted by the provided path 212 228 * 213 * @throws GUIModelException thrown in cases such as the GUI element object could not be214 * 215 */ 216 private IGUIElement integratePath(TreeNode 229 * @throws GUIModelException 230 * thrown in cases such as the GUI element object could not be instantiated 231 */ 232 private IGUIElement integratePath(TreeNode parentNode, 217 233 List<? extends IGUIElementSpec> remainingPath, 218 IGUIElementFactory 234 IGUIElementFactory guiElementFactory) 219 235 throws GUIModelException 220 236 { 221 237 IGUIElementSpec specToIntegrateElementFor = remainingPath.remove(0); 222 238 223 List<TreeNode> similarNodes = 224 determineSimilarChildNodes(parentNode, specToIntegrateElementFor); 225 226 if (similarNodes.size() > 1) { 227 // this may happen, if the GUI elements changed over time (e.g. their name is usually 228 // set later in the program execution) and if they now match because of the changes. 229 // So perform a merge of all similar children of the current parent node to reduce the 230 // model and then try the determination of matching children again. 231 mergeSimilarChildren(parentNode); 232 similarNodes = determineSimilarChildNodes(parentNode, specToIntegrateElementFor); 233 234 if (similarNodes.size() > 1) { 235 // this can happen, because the similarity check is not transitive. The new GUI 236 // element may be similar to two or more existing ones, but the existing ones 237 // may not be similar to each other. As an example, the new GUI element may 238 // not yet provide sufficient information (such as all names it will have during 239 // the execution of the program). Therefore the similarity check with GUI elements 240 // that already contain more information may return true. But the similarity check 241 // of two GUI elements that already carry a lot of information may return false, 242 // although both are similar to the new GUI element. Therefore, we try a selection 243 // based on the children of the existing and new GUI elements. The one for which 244 // the existing children match best is selected to be the right one. 245 similarNodes = considerSubChildren(similarNodes, remainingPath); 246 247 if (similarNodes.size() > 1) { 248 Console.traceln(Level.WARNING, "TODO: implement handling to many similar" + 249 "children: " + specToIntegrateElementFor); 250 for (TreeNode similarNode : similarNodes) { 251 Console.traceln(Level.WARNING, " " + similarNode.guiElement); 252 } 253 Console.traceln(Level.WARNING, ""); 254 } 255 } 256 } 257 else if (similarNodes.size() == 1) { 258 similarNodes.get(0).guiElement.updateSpecification(specToIntegrateElementFor); 259 } 260 else if (similarNodes.size() == 0) { 261 // if we get here, the corresponding path does not exist yet. So create it 262 IGUIElement newElement = guiElementFactory.instantiateGUIElement 263 (specToIntegrateElementFor, parentNode.guiElement); 264 265 similarNodes.add(parentNode.addChild(newElement)); 266 } 267 239 TreeNode child = findEqualChild(parentNode, specToIntegrateElementFor); 240 if (child != null) { 241 // TODO this call should be deprecated and change nothing 242 child.guiElement.updateSpecification(specToIntegrateElementFor); 243 } 244 else { 245 IGUIElement newElement = 246 guiElementFactory.instantiateGUIElement(specToIntegrateElementFor, 247 parentNode.guiElement); 248 249 child = parentNode.addChild(newElement); 250 } 251 268 252 if (remainingPath.size() > 0) { 269 return integratePath( similarNodes.get(0), remainingPath, guiElementFactory);253 return integratePath(child, remainingPath, guiElementFactory); 270 254 } 271 255 else { 272 return similarNodes.get(0).guiElement; 273 } 274 } 275 276 /** 277 * <p> 278 * Determines all children of the provided node, which are similar to the provided 279 * specification. 280 * </p> 281 */ 282 private List<TreeNode> determineSimilarChildNodes(TreeNode parentNode, 283 IGUIElementSpec specToMatch) 284 { 285 List<TreeNode> similarChildren = new ArrayList<TreeNode>(); 286 256 return child.guiElement; 257 } 258 } 259 260 /** 261 * <p> 262 * TODO: comment 263 * </p> 264 * 265 * @param parentNode 266 * @param specToMatch 267 * @return 268 */ 269 private TreeNode findEqualChild(TreeNode parentNode, IGUIElementSpec specToMatch) { 287 270 if (parentNode.children != null) { 288 271 for (TreeNode child : parentNode.children) { 289 if (specToMatch. getSimilarity(child.guiElement.getSpecification())) {290 similarChildren.add(child);272 if (specToMatch.equals(child.guiElement.getSpecification())) { 273 return child; 291 274 } 292 275 } 293 276 } 294 295 return similarChildren; 296 } 297 298 /** 299 * <p> 300 * This method is called in the case, that several child nodes of a parent node are similar 301 * to a node to be integrated into the model. This method tries to determine the similar child 302 * nodes of which the sub children match best to the further path to be integrated. This method 303 * does nothing, if the similar children do not have children yet of if the remaining path 304 * does not denote further children. 305 * </p> 306 * 307 * @return a hopefully reduced list of similar nodes based on their children. 308 */ 309 private List<TreeNode> considerSubChildren(List<TreeNode> similarNodes, 310 List<? extends IGUIElementSpec> remainingPath) 311 { 312 List<TreeNode> reducedList = new ArrayList<TreeNode>(); 313 314 // check, if there are any children to consider and remove any similar node, that has 315 // further children 316 if (remainingPath.size() <= 0) { 317 for (TreeNode similarNode : similarNodes) { 318 if ((similarNode.children == null) || (similarNode.children.size() == 0)) { 319 reducedList.add(similarNode); 320 } 321 } 322 } 323 else { 324 // if there are further children to consider, then check if there is already a child 325 // node that has an appropriate child 326 IGUIElementSpec subChildSpec = remainingPath.get(0); 327 for (TreeNode similarNode : similarNodes) { 328 if (similarNode.children != null) { 329 for (TreeNode subchild : similarNode.children) { 330 if (subchild.guiElement.getSpecification().getSimilarity(subChildSpec)) { 331 reducedList.add(similarNode); 332 break; 333 } 277 return null; 278 } 279 280 /** 281 * <p> 282 * merges similar children of a parent node. The method compares all children of the parent node 283 * with each other. If two of them are similar, it merges them, registers them with each other 284 * for equality checks, and removes one of them from the list of children. 285 * </p> 286 */ 287 private void mergeSimilarChildren(TreeNode parentNode) { 288 if (parentNode.children == null || parentNode.children.isEmpty()) { 289 return; 290 } 291 292 // lets first merge the grandchildren 293 for (TreeNode child : parentNode.children) { 294 mergeSimilarChildren(child); 295 } 296 297 boolean performedMerge; 298 299 do { 300 performedMerge = false; 301 for (int i = 0; !performedMerge && i < parentNode.children.size(); i++) { 302 IGUIElementSpec elemSpec1 = 303 parentNode.children.get(i).guiElement.getSpecification(); 304 for (int j = i + 1; !performedMerge && j < parentNode.children.size(); j++) { 305 IGUIElementSpec elemSpec2 = 306 parentNode.children.get(j).guiElement.getSpecification(); 307 if (elemSpec1.getSimilarity(elemSpec2)) { 308 TreeNode replacement = 309 mergeTreeNodes(parentNode.children.get(i), parentNode.children.get(j)); 310 311 parentNode.children.set(i, replacement); 312 parentNode.children.remove(j); 313 performedMerge = true; 314 i--; 315 break; 334 316 } 335 317 } 336 318 } 337 319 } 338 339 return reducedList;340 }341 342 /**343 * <p>344 * merges similar children of a parent node. The method compares all children of the parent345 * node with each other. If two of them are similar, it merges them, registers them with each346 * other for equality checks, and removes one of them from the list of children.347 * </p>348 */349 private void mergeSimilarChildren(TreeNode parentNode) {350 boolean performedMerge;351 352 do {353 performedMerge = false;354 if (parentNode.children != null) {355 for (int i = 0; (!performedMerge) && (i < parentNode.children.size()); i++) {356 for (int j = i + 1; j < parentNode.children.size(); j++) {357 IGUIElement elem1 = parentNode.children.get(i).guiElement;358 IGUIElement elem2 = parentNode.children.get(j).guiElement;359 if (elem1.getSpecification().getSimilarity(elem2.getSpecification())) {360 TreeNode replacement = mergeTreeNodes(parentNode.children.get(i),361 parentNode.children.get(j));362 363 parentNode.children.set(i, replacement);364 parentNode.children.remove(j);365 performedMerge = true;366 break;367 }368 }369 }370 }371 }372 320 while (performedMerge); 373 321 } … … 375 323 /** 376 324 * <p> 377 * merges two nodes with each other. Merging means registering the GUI element objects with 378 * each other for equality checks. Further it add all children of both nodes to a new379 * replacing node.Afterwards, all similar nodes of the replacement node are merged as well.380 * </p> 381 * 325 * merges two nodes with each other. Merging means registering the GUI element objects with each 326 * other for equality checks. Further it add all children of both nodes to a new replacing node. 327 * Afterwards, all similar nodes of the replacement node are merged as well. 328 * </p> 329 * 382 330 * @return a tree node being the merge of the two provided nodes. 383 331 */ 384 332 private TreeNode mergeTreeNodes(TreeNode treeNode1, TreeNode treeNode2) { 385 TreeNode replacement = new TreeNode();386 387 replacement.guiElement = treeNode1.guiElement;388 389 333 // the following two lines are needed to preserve the references to the existing GUI 390 334 // elements. If two elements are the same, one should be deleted to make the elements … … 392 336 // these, we simply register the equal GUI elements with each other so that an equals 393 337 // check can return true. 394 replacement.guiElement.addEqualGUIElement(treeNode2.guiElement); 395 treeNode2.guiElement.addEqualGUIElement(replacement.guiElement); 396 338 treeNode1.guiElement.addEqualGUIElement(treeNode2.guiElement); 339 treeNode2.guiElement.addEqualGUIElement(treeNode1.guiElement); 340 341 // and now a replacement node that is the merge of treeNode1 and treeNode2 is created 342 TreeNode replacement = new TreeNode(); 343 replacement.guiElement = treeNode1.guiElement; 397 344 if (treeNode1.children != null) { 398 345 for (TreeNode child : treeNode1.children) { 399 replacement.addChild(child.guiElement);400 }401 }402 346 // replacement.addChild(child.guiElement); 347 replacement.addChildNode(child); 348 } 349 } 403 350 if (treeNode2.children != null) { 404 351 for (TreeNode child : treeNode2.children) { 405 replacement.addChild(child.guiElement); 406 } 407 } 408 352 // replacement.addChild(child.guiElement); 353 replacement.addChildNode(child); 354 } 355 } 356 409 357 mergeSimilarChildren(replacement); 410 358 411 359 replacement.guiElement.updateSpecification(treeNode2.guiElement.getSpecification()); 412 360 361 // finally, update the known nodes list 362 // if you don't do this getChildren will return wrong things and very bad things happen! 363 allNodes.remove(treeNode1); 364 allNodes.remove(treeNode2); 365 allNodes.add(replacement); 366 413 367 return replacement; 414 368 } … … 416 370 /** 417 371 * <p> 418 * dumps a GUI element to the stream. A dump contains the toString() representation of the 419 * GUIelement as well as a indented list of its children surrounded by braces.372 * dumps a GUI element to the stream. A dump contains the toString() representation of the GUI 373 * element as well as a indented list of its children surrounded by braces. 420 374 * </p> 421 375 */ … … 445 399 * </p> 446 400 */ 447 private class TreeNode 448 { 401 private class TreeNode { 449 402 /** */ 450 403 private IGUIElement guiElement; 451 404 452 405 /** */ 453 406 private List<TreeNode> children; 454 407 455 408 /** */ 456 // private TreeNode parent;457 409 // private TreeNode parent; 410 458 411 /** 459 412 * <p> … … 461 414 * </p> 462 415 */ 463 private TreeNode addChild(IGUIElement guiElement) 464 { 465 if (children == null) 466 { 416 private TreeNode addChild(IGUIElement guiElement) { 417 if (children == null) { 467 418 children = new ArrayList<TreeNode>(); 468 419 } 469 420 470 421 TreeNode child = new TreeNode(); 471 422 child.guiElement = guiElement; 472 // child.parent = this;423 // child.parent = this; 473 424 children.add(child); 474 425 475 426 allNodes.add(child); 476 427 477 428 return child; 478 429 } 479 430 480 /* (non-Javadoc) 431 /** 432 * 433 * <p> 434 * TODO: comment 435 * </p> 436 * 437 * @param node 438 * @return 439 */ 440 private TreeNode addChildNode(TreeNode node) { 441 if (children == null) { 442 children = new ArrayList<TreeNode>(); 443 } 444 children.add(node); 445 return node; 446 } 447 448 /* 449 * (non-Javadoc) 450 * 481 451 * @see java.lang.Object#toString() 482 452 */
Note: See TracChangeset
for help on using the changeset viewer.