Changeset 619 for trunk/quest-plugin-mfc/src/main
- Timestamp:
- 08/27/12 11:45:09 (12 years ago)
- Location:
- trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc
- Files:
-
- 17 added
- 6 deleted
- 10 edited
- 4 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/EventGenerator.java
r581 r619 1 1 2 package de.ugoe.cs.quest.plugin.mfc; 2 3 3 4 import java.io.IOException; 4 5 import java.security.InvalidParameterException; 6 import java.util.ArrayList; 5 7 import java.util.HashMap; 6 8 import java.util.Iterator; … … 18 20 19 21 import de.ugoe.cs.quest.eventcore.Event; 20 import de.ugoe.cs.quest.plugin.mfc.eventcore.MFCEventTarget; 21 import de.ugoe.cs.quest.plugin.mfc.eventcore.MFCEventType; 22 import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowTree; 23 import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowTreeNode; 22 import de.ugoe.cs.quest.eventcore.IEventType; 23 import de.ugoe.cs.quest.plugin.mfc.EventGenerationRule.Term; 24 import de.ugoe.cs.quest.plugin.mfc.eventcore.MFCEvent; 25 import de.ugoe.cs.quest.plugin.mfc.eventcore.MFCEventTypeFactory; 26 import de.ugoe.cs.quest.plugin.mfc.eventcore.ReplayWindowsMessage; 24 27 import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessage; 28 import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessageType; 29 import de.ugoe.cs.quest.plugin.mfc.guimodel.MFCGUIElement; 30 import de.ugoe.cs.quest.plugin.mfc.guimodel.WindowTree; 25 31 import de.ugoe.cs.util.console.Console; 26 32 27 33 /** 28 34 * <p> 29 * Translates sequences of windows messages into {@link WindowsEvent}s that can 30 * be used by theEventBench core libraries.35 * Translates sequences of windows messages into {@link WindowsEvent}s that can be used by the 36 * EventBench core libraries. 31 37 * </p> 32 38 * … … 36 42 public class EventGenerator { 37 43 38 /** 39 * <p> 40 * Helper method that fetches the document node of an XML file. 41 * </p> 42 * 43 * @param filename 44 * name of the XML file 45 * @return the document node 46 */ 47 private static Document getDocument(String filename) { 48 SAXBuilder builder = new SAXBuilder(); 49 Document doc = null; 50 51 try { 52 doc = builder.build(filename); 53 rulesNamespace = Namespace.getNamespace("ul:rules"); 54 } catch (JDOMException e) { 55 Console.printerrln("Invalid rules file."); 56 e.printStackTrace(); 57 } catch (IOException e) { 58 Console.printerrln("Invalid rules file."); 59 e.printStackTrace(); 60 } 61 62 return doc; 63 } 64 65 /** 66 * <p> 67 * Name and path of the XML files containing the rules. 68 * </p> 69 */ 70 private String rulesFile; 71 72 /** 73 * <p> 74 * Iterator used for the current sequence. 75 * </p> 76 */ 77 private ListIterator<WindowsMessage> sequenceIterator; 78 79 /** 80 * <p> 81 * Token that is currently being generated. 82 * </p> 83 */ 84 private Event currentToken; 85 86 /** 87 * <p> 88 * Event type of the current token. Stored as a member to be able to update it during the parsing of the idinfo tag. 89 * </p> 90 */ 91 private MFCEventType currentType; 92 93 /** 94 * <p> 95 * Reference to the ul:rules namespace. 96 * </p> 97 */ 98 private static Namespace rulesNamespace; 99 100 /** 101 * <p> 102 * The name of the rule that is currently being evaluated. 103 * </p> 104 */ 105 private String currentRuleName; 106 107 /** 108 * <p> 109 * Internal message storage. Used to implement the 110 * <code>{@literal <store>}</code> and <code>{@literal <storeSeq>}</code> 111 * tags. 112 * </p> 113 */ 114 private Map<String, Object> messageStorage; 115 116 /** 117 * <p> 118 * Creates a new EventGenerator. Sets "data/rules.xml" as default file for 119 * the rules. 120 * </p> 121 */ 122 public EventGenerator() { 123 rulesFile = "data/rules.xml"; 124 } 125 126 /** 127 * <p> 128 * Tries to match the rules to the given sequence to generate an 129 * {@link WindowsEvent}. 130 * </p> 131 * <p> 132 * The rules are matched the order, in which they are defined in the XML 133 * file. Therefore, the order of the rules in the file defines priorities, 134 * when multiple rules could be matched to the same sequence. 135 * </p> 136 * 137 * @param sequence 138 * sequence of message for which an event will be generated 139 * @return event that matches the messages; null, if no rule can be matched 140 */ 141 @SuppressWarnings("unchecked") 142 public Event generateEvent(List<WindowsMessage> sequence) { 143 Document rulesDoc = getDocument(rulesFile); 144 Element rulesRoot = rulesDoc.getRootElement(); 145 146 List<Element> ruleElements = rulesRoot.getChildren("rule", 147 rulesNamespace); 148 149 boolean isMatch = false; 150 151 for (int ruleIndex = 0; ruleIndex < ruleElements.size() && !isMatch; ruleIndex++) { 152 Element currentRule = ruleElements.get(ruleIndex); 153 currentRuleName = currentRule.getAttributeValue("name"); 154 currentType = new MFCEventType(currentRuleName); 155 currentToken = new Event(currentType); 156 157 isMatch = true; 158 messageStorage = new HashMap<String, Object>(); 159 sequenceIterator = sequence.listIterator(); 160 List<Element> ruleChildrenMsg = currentRule.getChildren("msg", 161 rulesNamespace); 162 163 int i = 0; 164 while (isMatch && i < ruleChildrenMsg.size()) { 165 Element messageElement = ruleChildrenMsg.get(i); 166 if ("true".equals(messageElement.getAttributeValue("multiple"))) { 167 Element nextMessageElement = null; 168 if (i + 1 < ruleChildrenMsg.size()) { 169 nextMessageElement = ruleChildrenMsg.get(i + 1); 170 } 171 try { 172 isMatch = matchMultipleMessages(messageElement, 173 nextMessageElement); 174 } catch (InvalidParameterException e) { 175 Console.printerrln(e.getMessage()); 176 } 177 } else { 178 try { 179 isMatch = matchSingleMessage(messageElement); 180 } catch (InvalidParameterException e) { 181 Console.printerrln(e.getMessage()); 182 } 183 } 184 i++; 185 } 186 if (isMatch) { 187 List<Element> ruleChildren = currentRule.getChildren(); 188 for (Element genMsgElement : ruleChildren) { 189 if (genMsgElement.getName().equals("genMsg")) { 190 try { 191 generateReplayMessage(genMsgElement); 192 } catch (InvalidParameterException e) { 193 Console.printerrln(e.getMessage()); 194 // TODO currentToken.invalidateReplay(); 195 } 196 } else if (genMsgElement.getName().equals("genMsgSeq")) { 197 try { 198 generateReplaySequence(genMsgElement); 199 // TODO currentToken.invalidateReplay(); 200 } catch (InvalidParameterException e) { 201 Console.printerrln(e.getMessage()); 202 // TODO currentToken.invalidateReplay(); 203 } 204 } 205 } 206 Element idinfoElement = currentRule.getChild("idinfo", 207 rulesNamespace); 208 if (idinfoElement != null) { 209 // cannot be empty if document is valid 210 List<Element> valueElements = idinfoElement.getChildren(); 211 currentType.setInfo(getTermValue(null, 212 valueElements.get(0))); 213 } 214 Console.traceln(currentToken.getType().toString() + " matched"); 215 } else { 216 currentToken = null; 217 } 218 } 219 if (!isMatch) { 220 Console.traceln("no match found for sequence: " 221 + sequence.toString()); 222 } 223 return currentToken; 224 } 225 226 // //////////////////////////////////////////////////////////// 227 // Helper functions for matching of events, i.e., msg-nodes // 228 // //////////////////////////////////////////////////////////// 229 230 /** 231 * <p> 232 * Handles msg-nodes where multiple is not true, i.e., not a sequences. 233 * </p> 234 * 235 * @param messageElement 236 * {@link Element} representing the msg-node 237 * @return true, if a match is found; false otherwise 238 */ 239 private boolean matchSingleMessage(Element messageElement) { 240 boolean isMatch = false; 241 WindowsMessage currentMessage = null; 242 243 int type = Integer.parseInt(messageElement.getAttributeValue("type")); 244 245 while (!isMatch && sequenceIterator.hasNext()) { 246 /* 247 * traverses the messages from the current position forward till a 248 * message with the correct type is found 249 */ 250 currentMessage = sequenceIterator.next(); 251 if (type == currentMessage.getType()) { 252 // message with the correct type found 253 // eval child nodes for further matching/storing 254 isMatch = evalEqualRestrictions(currentMessage, messageElement); 255 256 // in case the message is a match, eval storage children 257 if (isMatch) { 258 handleStorage(messageElement, currentMessage); 259 currentToken.setTarget(new MFCEventTarget(currentMessage 260 .getXmlWindowDescription())); 261 // TODO currentToken.setTargetShort(currentMessage.getParentNames()); 262 } 263 } 264 } 265 266 return isMatch; 267 } 268 269 /** 270 * <p> 271 * Handles msg-nodes where multiple is true, i.e., sequences. Requires 272 * knowledge about the next msg-node to determine the end of the sequence. 273 * </p> 274 * 275 * @param messageElement 276 * {@link Element} representing the msg-node 277 * @param nextMessageElement 278 * {@link Element} representing the next msg-node; {@code null} 279 * if the current node is the last one 280 * @return true, if a sequence is matched; false otherwise 281 */ 282 private boolean matchMultipleMessages(Element messageElement, 283 Element nextMessageElement) { 284 boolean isMatch = false; 285 boolean isCurrentMatch = false; 286 boolean nextMatchFound = false; 287 WindowsMessage currentMessage = null; 288 WindowsMessage nextMessage = null; 289 290 int type = Integer.parseInt(messageElement.getAttributeValue("type")); 291 292 int nextType = -1; 293 if (nextMessageElement != null) { 294 nextType = Integer.parseInt(nextMessageElement 295 .getAttributeValue("type")); 296 } 297 298 while (!nextMatchFound && sequenceIterator.hasNext()) { 299 currentMessage = sequenceIterator.next(); 300 if (type == currentMessage.getType()) { 301 isCurrentMatch = evalEqualRestrictions(currentMessage, 302 messageElement); 303 isMatch = isMatch || isCurrentMatch; 304 305 if (isCurrentMatch) { 306 handleStorage(messageElement, currentMessage); 307 currentToken.setTarget(new MFCEventTarget(currentMessage 308 .getXmlWindowDescription())); 309 // TODO currentToken.setTargetShort(currentMessage.getParentNames()); 310 } 311 } 312 if (nextMessageElement != null && isMatch) { 313 // peek next message to check if the sequence ends and the next 314 // match is found 315 if (!sequenceIterator.hasNext()) { 316 return false; // sequence is over, but not all messages are 317 // found 318 } 319 nextMessage = sequenceIterator.next(); 320 sequenceIterator.previous(); 321 322 if (nextType == nextMessage.getType()) { 323 nextMatchFound = evalEqualRestrictions(nextMessage, 324 nextMessageElement); 325 } 326 327 } 328 } 329 330 return isMatch; 331 } 332 333 /** 334 * <p> 335 * Handles equals-nodes. 336 * </p> 337 * 338 * @param currentMessage 339 * {@link Element} representing the msg-node the equals-node 340 * belongs to 341 * @param messageElement 342 * {@link Element} representing the equals-node to be evaluated 343 * @return true, if constraint is fulfilled; false otherwise 344 */ 345 @SuppressWarnings("unchecked") 346 private boolean evalEqualRestrictions(WindowsMessage currentMessage, 347 Element messageElement) { 348 boolean isMatch = true; 349 for (Element childElement : (List<Element>) messageElement.getChildren( 350 "equals", rulesNamespace)) { 351 List<Element> termElements = childElement.getChildren(); 352 // the size 2 of termElements is guaranteed by the XML schema 353 String value1 = getTermValue(currentMessage, termElements.get(0)); 354 String value2 = getTermValue(currentMessage, termElements.get(1)); 355 if (value1 == null || value2 == null) { 356 isMatch = false; 357 } else { 358 isMatch = isMatch && value1.equals(value2); 359 } 360 } 361 for (Element childElement : (List<Element>) messageElement.getChildren( 362 "equalsSeq", rulesNamespace)) { 363 List<Element> termElements = childElement.getChildren(); 364 List<String> values1 = getTermValueSeq(termElements.get(0)); 365 List<String> values2 = getTermValueSeq(termElements.get(0)); 366 if (values1 == null || values2 == null) { 367 isMatch = false; 368 } else { 369 isMatch = isMatch && values1.equals(values2); 370 } 371 } 372 return isMatch; 373 } 374 375 /** 376 * <p> 377 * Handles store-nodes and storeSeq-nodes. 378 * </p> 379 * 380 * @param messageElement 381 * {@link Element} representing the msg-node that is currently 382 * being evaluated 383 * @param currentMessage 384 * current message in the message sequence that is matched; this 385 * is the message that is stored 386 */ 387 @SuppressWarnings("unchecked") 388 private void handleStorage(Element messageElement, 389 WindowsMessage currentMessage) { 390 for (Element childElement : (List<Element>) messageElement.getChildren( 391 "store", rulesNamespace)) { 392 String identifier = childElement.getAttributeValue("var"); 393 messageStorage.put(identifier, currentMessage); 394 resolveHwnd(currentMessage, childElement); 395 } 396 for (Element childElement : (List<Element>) messageElement.getChildren( 397 "storeSeq", rulesNamespace)) { 398 String identifier = childElement.getAttributeValue("varSeq"); 399 Object tmp = messageStorage.get(identifier); 400 List<WindowsMessage> storedSequence; 401 if (tmp == null || tmp instanceof WindowsMessage) { 402 storedSequence = new LinkedList<WindowsMessage>(); 403 storedSequence.add(currentMessage); 404 messageStorage.put(identifier, storedSequence); 405 } else if (tmp instanceof List<?>) { 406 storedSequence = (List<WindowsMessage>) tmp; 407 storedSequence.add(currentMessage); 408 messageStorage.put(identifier, storedSequence); 409 } 410 resolveHwnd(currentMessage, childElement); 411 } 412 } 413 414 /** 415 * <p> 416 * Resolves a parameter that contains a HWND of a message to the target 417 * string of the HWND and stores it. 418 * </p> 419 * 420 * @param currentMessage 421 * message whose HWND is resolved 422 * @param childElement 423 * child element of the store node that represents the resolve 424 */ 425 @SuppressWarnings("unchecked") 426 private void resolveHwnd(WindowsMessage currentMessage, Element childElement) { 427 List<Element> resolveElements = childElement.getChildren("resolveHwnd", 428 rulesNamespace); 429 for (Element resolveElement : resolveElements) { 430 String param = resolveElement.getAttributeValue("param"); 431 String storeParam = resolveElement.getAttributeValue("storeParam"); 432 int paramHwnd = Integer 433 .parseInt(currentMessage.getParameter(param)); 434 WindowTreeNode node = WindowTree.getInstance().find(paramHwnd); 435 if (node != null) { 436 currentMessage.addParameter(storeParam, 437 node.xmlRepresentation()); 438 } 439 } 440 } 441 442 // ///////////////////////////////////////////////////// 443 // Helper functions for generating the replay, i.e., 444 // parsing of genMsg und genMsgSeq-nodes 445 // ///////////////////////////////////////////////////// 446 447 /** 448 * <p> 449 * Handles genMsg-nodes and adds the replay to the {@link Event} that is 450 * generated. 451 * </p> 452 * 453 * @param genMsgElement 454 * {@link Element} representing the genMsg-node 455 */ 456 @SuppressWarnings("unchecked") 457 private void generateReplayMessage(Element genMsgElement) { 458 List<Element> genMsgChildren = genMsgElement.getChildren(); 459 WindowsMessage generatedMessage = null; 460 if (genMsgChildren.size() == 1) { // replay stored message without 461 // change 462 String obj = genMsgChildren.get(0).getAttributeValue("obj"); 463 generatedMessage = getStoredMessageVariable(null, obj); 464 } else { // generate message according to the rule 465 for (Element genMsgChild : genMsgChildren) { 466 Element termElement = (Element) genMsgChild.getChildren() 467 .get(0); 468 if (genMsgChild.getName().equals("type")) { 469 try { 470 int msgType = Integer.parseInt(getTermValue(null, 471 termElement)); 472 generatedMessage = new WindowsMessage(msgType); 473 } catch (NumberFormatException e) { 474 throw new InvalidParameterException( 475 "Failure generating replay sequence for rule " 476 + currentRuleName 477 + ": Defined type is not an integer."); 478 } 479 } else if (genMsgChild.getName().equals("target")) { 480 String targetString = getTermValue(null, termElement); 481 generatedMessage.setXmlWindowDescription(targetString); 482 } else if (genMsgChild.getName().equals("LPARAM")) { 483 String paramValueStr = getTermValue(null, termElement); 484 long paramValue = 0; 485 Element loword = genMsgChild.getChild("LOWORD", 486 rulesNamespace); 487 if (loword != null) { 488 paramValue = loHiWord(genMsgChild); 489 generatedMessage.setLPARAM(paramValue); 490 } else { 491 try { 492 paramValue = Integer.parseInt(paramValueStr); 493 generatedMessage.setLPARAM(paramValue); 494 } catch (NumberFormatException e) { 495 generatedMessage 496 .setLPARAMasWindowDesc(paramValueStr); 497 } 498 } 499 } else if (genMsgChild.getName().equals("WPARAM")) { 500 String paramValueStr = getTermValue(null, termElement); 501 long paramValue = 0; 502 Element loword = genMsgChild.getChild("LOWORD", 503 rulesNamespace); 504 if (loword != null) { 505 paramValue = loHiWord(genMsgChild); 506 generatedMessage.setWPARAM(paramValue); 507 } else { 508 try { 509 paramValue = Integer.parseInt(paramValueStr); 510 generatedMessage.setWPARAM(paramValue); 511 } catch (NumberFormatException e) { 512 generatedMessage 513 .setWPARAMasWindowDesc(paramValueStr); 514 } 515 } 516 } 517 } 518 } 519 if (generatedMessage != null) { 520 int delay = Integer.parseInt(genMsgElement 521 .getAttributeValue("delay")); 522 generatedMessage.setDelay(delay); 523 } else { 524 // TODO currentToken.invalidateReplay(); 525 } 526 currentToken.addReplayable(generatedMessage); 527 } 528 529 /** 530 * Handles genMsgSeq-nodes and adds the replay to the {@link Event} that is 531 * generated.</p> 532 * 533 * @param genMsgElement 534 * {@link Element} representing the genMsgSeq-node. 535 */ 536 @SuppressWarnings("unchecked") 537 private void generateReplaySequence(Element genMsgElement) { 538 List<Element> genMsgSeqChildren = genMsgElement.getChildren(); 539 List<WindowsMessage> generatedMessageSeq = new LinkedList<WindowsMessage>(); 540 if (genMsgSeqChildren.size() == 1) { 541 String obj = genMsgSeqChildren.get(0).getAttributeValue("seqObj"); 542 generatedMessageSeq = getStoredSeqVariable(obj); 543 } else { 544 boolean msgsGenerated = false; 545 int constMsgType = 0; 546 for (Element genMsgSeqChild : genMsgSeqChildren) { 547 Element termElement = (Element) genMsgSeqChild.getChildren() 548 .get(0); 549 if (genMsgSeqChild.getName().equals("type")) { 550 // note: cannot easily be extracted because of mulitple 551 // return values 552 if (termElement.getName().equals("seqValue")) { 553 String obj = termElement.getAttributeValue("seqObj"); 554 List<WindowsMessage> seqVar = getStoredSeqVariable(obj); 555 for (WindowsMessage msg : seqVar) { 556 generatedMessageSeq.add(new WindowsMessage(msg 557 .getType())); 558 } 559 msgsGenerated = true; 560 } else { // constValue type 561 constMsgType = Integer.parseInt(getTermValue(null, 562 termElement)); 563 } 564 } else if (genMsgSeqChild.getName().equals("target")) { 565 msgsGenerated = createSequenceTarget(generatedMessageSeq, 566 msgsGenerated, constMsgType, termElement); 567 } else if (genMsgSeqChild.getName().equals("LPARAM")) { 568 msgsGenerated = createSequenceLParam(generatedMessageSeq, 569 msgsGenerated, constMsgType, termElement); 570 } else if (genMsgSeqChild.getName().equals("WPARAM")) { 571 msgsGenerated = createSequenceWParam(generatedMessageSeq, 572 msgsGenerated, constMsgType, termElement); 573 } 574 } 575 } 576 currentToken.addReplayableSequence(generatedMessageSeq); 577 } 578 579 /** 580 * <p> 581 * Creates the targets for replay sequences generated with genMsgSeq-nodes. 582 * </p> 583 * 584 * @param generatedMessageSeq 585 * list of the messages that is being generated 586 * @param msgsGenerated 587 * boolean stating if the list of messages is already generated 588 * or if the generation has to be handles by this method 589 * @param constMsgType 590 * a constant message type that is used for message generation, 591 * in case the list of message is generated by this method 592 * @param termElement 593 * {@link Element} representing the term-node describing the 594 * target 595 * @return true, if the list of message is generated after calling this 596 * method; false otherwise 597 * @throws NoSuchElementException 598 * thrown if the seqVar referred to in the termElement contains 599 * a different number of messages than is contained in 600 * messageSeq 601 */ 602 private boolean createSequenceTarget( 603 List<WindowsMessage> generatedMessageSeq, boolean msgsGenerated, 604 int constMsgType, Element termElement) 605 throws NoSuchElementException { 606 Iterator<WindowsMessage> seqIterator = generatedMessageSeq.iterator(); 607 if (termElement.getName().equals("seqValue")) { 608 String obj = termElement.getAttributeValue("seqObj"); 609 List<WindowsMessage> seqVar = getStoredSeqVariable(obj); 610 if (msgsGenerated && seqVar.size() != generatedMessageSeq.size()) { 611 throw new InvalidParameterException( 612 "Failure generating replay sequence for rule " 613 + currentRuleName 614 + ": One or more of the sequence variables used to generate a sequence have different lenghts."); 615 } 616 for (WindowsMessage msg : seqVar) { 617 WindowsMessage currentSeqMsg = getCurrentSeqMsg( 618 generatedMessageSeq, msgsGenerated, constMsgType, 619 seqIterator); 620 String targetString = msg.getParameter(termElement 621 .getAttributeValue("param")); 622 currentSeqMsg.setXmlWindowDescription(targetString); 623 } 624 msgsGenerated = true; 625 } else { // const value 626 throw new AssertionError("target must be a sequence variable!"); 627 /* 628 * If target would not be a variable, the message-elements could not 629 * yet be created and the whole sequence might be broken. If this is 630 * to be changed, createSequenceLParam and createSequenceWParam need 631 * to be addepted, too. 632 */ 633 } 634 return msgsGenerated; 635 } 636 637 /** 638 * <p> 639 * Creates the LPARAMs for replay sequences generated with genMsgSeq-nodes. 640 * </p> 641 * 642 * @param generatedMessageSeq 643 * list of the messages that is being generated 644 * @param msgsGenerated 645 * boolean stating if the list of messages is already generated 646 * or if the generation has to be handles by this method 647 * @param constMsgType 648 * a constant message type that is used for message generation, 649 * in case the list of message is generated by this method 650 * @param termElement 651 * {@link Element} representing the term-node describing the 652 * LPARAM 653 * @return true, if the list of message is generated after calling this 654 * method; false otherwise 655 * @throws NoSuchElementException 656 * thrown if the seqVar referred to in the termElement contains 657 * a different number of messages than is contained in 658 * messageSeq 659 */ 660 private boolean createSequenceLParam( 661 List<WindowsMessage> generatedMessageSeq, boolean msgsGenerated, 662 int constMsgType, Element termElement) 663 throws NoSuchElementException { 664 Iterator<WindowsMessage> seqIterator = generatedMessageSeq.iterator(); 665 if (termElement.getName().equals("seqValue")) { 666 String obj = termElement.getAttributeValue("seqObj"); 667 List<WindowsMessage> seqVar = getStoredSeqVariable(obj); 668 if (msgsGenerated && seqVar.size() != generatedMessageSeq.size()) { 669 throw new InvalidParameterException( 670 "Failure generating replay sequence for rule " 671 + currentRuleName 672 + ": One or more of the sequence variables used to generate a sequence have different lenghts."); 673 } 674 for (WindowsMessage msg : seqVar) { 675 WindowsMessage currentSeqMsg = getCurrentSeqMsg( 676 generatedMessageSeq, msgsGenerated, constMsgType, 677 seqIterator); 678 String paramValueStr = msg.getParameter(termElement 679 .getAttributeValue("param")); 680 int paramValue = 0; 681 try { 682 paramValue = Integer.parseInt(paramValueStr); 683 currentSeqMsg.setLPARAM(paramValue); 684 } catch (NumberFormatException e) { 685 currentSeqMsg.setLPARAMasWindowDesc(paramValueStr); 686 } 687 } 688 if (seqIterator.hasNext()) { 689 // the first seq-var has a different number of elements than the 690 // current one 691 throw new NoSuchElementException(); 692 } 693 msgsGenerated = true; 694 } else { // const value 695 int paramValue = Integer.parseInt(getTermValue(null, termElement)); 696 while (seqIterator.hasNext()) { 697 seqIterator.next().setLPARAM(paramValue); 698 } 699 } 700 return msgsGenerated; 701 } 702 703 /** 704 * <p> 705 * Creates the WPARAMs for replay sequences generated with genMsgSeq-nodes. 706 * </p> 707 * 708 * @param generatedMessageSeq 709 * list of the messages that is being generated 710 * @param msgsGenerated 711 * boolean stating if the list of messages is already generated 712 * or if the generation has to be handles by this method 713 * @param constMsgType 714 * a constant message type that is used for message generation, 715 * in case the list of message is generated by this method 716 * @param termElement 717 * {@link Element} representing the term-node describing the 718 * WPARAM 719 * @return true, if the list of message is generated after calling this 720 * method; false otherwise 721 * @throws NoSuchElementException 722 * thrown if the seqVar referred to in the termElement contains 723 * a different number of messages than is contained in 724 * messageSeq 725 */ 726 private boolean createSequenceWParam( 727 List<WindowsMessage> generatedMessageSeq, boolean msgsGenerated, 728 int constMsgType, Element termElement) 729 throws NoSuchElementException { 730 Iterator<WindowsMessage> seqIterator = generatedMessageSeq.iterator(); 731 if (termElement.getName().equals("seqValue")) { 732 String obj = termElement.getAttributeValue("seqObj"); 733 List<WindowsMessage> seqVar = getStoredSeqVariable(obj); 734 if (msgsGenerated && seqVar.size() != generatedMessageSeq.size()) { 735 throw new InvalidParameterException( 736 "Failure generating replay sequence for rule " 737 + currentRuleName 738 + ": One or more of the sequence variables used to generate a sequence have different lenghts."); 739 } 740 for (WindowsMessage msg : seqVar) { 741 WindowsMessage currentSeqMsg = getCurrentSeqMsg( 742 generatedMessageSeq, msgsGenerated, constMsgType, 743 seqIterator); 744 String paramValueStr = msg.getParameter(termElement 745 .getAttributeValue("param")); 746 int paramValue = 0; 747 try { 748 paramValue = Integer.parseInt(paramValueStr); 749 currentSeqMsg.setWPARAM(paramValue); 750 } catch (NumberFormatException e) { 751 currentSeqMsg.setWPARAMasWindowDesc(paramValueStr); 752 } 753 } 754 if (seqIterator.hasNext()) { 755 // the first seq-var has a different number of elements than the 756 // current one 757 throw new NoSuchElementException(); 758 } 759 msgsGenerated = true; 760 } else { // const value 761 int paramValue = Integer.parseInt(getTermValue(null, termElement)); 762 while (seqIterator.hasNext()) { 763 seqIterator.next().setWPARAM(paramValue); 764 } 765 } 766 return msgsGenerated; 767 } 768 769 /** 770 * <p> 771 * If a message sequence is already generated, i.e., msgsGenerated is true, 772 * the seqIterator is used to iterate through these messages and return the 773 * current one. If the message sequence is not yet generated, i.e., 774 * msgsGenerated is false, the message sequence is generated on the fly 775 * during each call of this message and the newly generated messages are 776 * returned. 777 * </p> 778 * 779 * @param generatedMessageSeq 780 * message sequence 781 * @param msgsGenerated 782 * indicates if generatedMessageSeq is already generated or has 783 * to be generated on the fly by this method 784 * @param constMsgType 785 * type of the message to be used for message generation 786 * @param seqIterator 787 * iterates through an already generated message sequence; must 788 * not be {@code null}, if msgsGenerated is true 789 * @return current message 790 */ 791 private WindowsMessage getCurrentSeqMsg( 792 List<WindowsMessage> generatedMessageSeq, boolean msgsGenerated, 793 int constMsgType, Iterator<WindowsMessage> seqIterator) { 794 WindowsMessage currentSeqMsg = null; 795 if (msgsGenerated) { 796 currentSeqMsg = seqIterator.next(); 797 } else { 798 currentSeqMsg = new WindowsMessage(constMsgType); 799 generatedMessageSeq.add(currentSeqMsg); 800 } 801 return currentSeqMsg; 802 } 803 804 // //////////////////////////// 805 // General helper functions // 806 // //////////////////////////// 807 808 /** 809 * <p> 810 * Retrieves a message from the storage for, e.g., comparison or replay. 811 * "this" is used to refer to the current message. 812 * </p> 813 * 814 * @param currentMessage 815 * current message during the parsing; passed to handle "this" 816 * @param obj 817 * object identifier in the storage 818 * @return message retrieved from the storage 819 * @throws InvalidParameterException 820 * thrown in case of invalid uses of "this" or if no message 821 * with the identifier obj is found in the storage 822 */ 823 private WindowsMessage getStoredMessageVariable( 824 WindowsMessage currentMessage, String obj) 825 throws InvalidParameterException { 826 WindowsMessage varMessage = null; 827 if (obj.equals("this")) { 828 if (currentMessage == null) { 829 throw new InvalidParameterException( 830 "Failure obtaining term value for rule " 831 + currentRuleName 832 + ": \"this\" is not a valid name for generating runtime messages."); 833 } 834 varMessage = currentMessage; 835 } else { 836 Object tmp = messageStorage.get(obj); 837 if (tmp instanceof WindowsMessage) { 838 varMessage = (WindowsMessage) tmp; 839 } else { 840 throw new InvalidParameterException( 841 "Failure obtaining term value for rule " 842 + currentRuleName + ": No message \"" + obj 843 + "\" stored."); 844 } 845 } 846 return varMessage; 847 } 848 849 /** 850 * <p> 851 * Retrieves a stored message sequence from the storage. 852 * </p> 853 * 854 * @param obj 855 * object identifier in the storage 856 * @return message sequence retrieved from the storage 857 * @throws InvalidParameterException 858 * thrown if no message sequences with the identifier obj is 859 * found in the storage 860 */ 861 @SuppressWarnings("unchecked") 862 private List<WindowsMessage> getStoredSeqVariable(String obj) 863 throws InvalidParameterException { 864 List<WindowsMessage> varMsgSeq = null; 865 Object tmp = messageStorage.get(obj); 866 if (tmp instanceof List<?>) { 867 varMsgSeq = (List<WindowsMessage>) tmp; 868 } else { 869 throw new InvalidParameterException( 870 "Failure obtaining term value for rule " + currentRuleName 871 + ": No sequence \"" + obj + "\" store."); 872 } 873 return varMsgSeq; 874 } 875 876 /** 877 * <p> 878 * Handles term-nodes and returns the value of the described term. 879 * </p> 880 * 881 * @param currentMessage 882 * current message during the parsing; required to resolve 883 * references to "this" in a term 884 * @param termElement 885 * {@link Element} representing the term node 886 * @return value of the term or {@code null} of the term node could not be 887 * evaluated 888 */ 889 private String getTermValue(WindowsMessage currentMessage, 890 Element termElement) { 891 String value = null; 892 WindowsMessage varMessage = null; 893 if (termElement.getName().equals("constValue")) { 894 value = termElement.getAttributeValue("value"); 895 } else if (termElement.getName().equals("paramValue")) { 896 String objectName = termElement.getAttributeValue("obj"); 897 varMessage = getStoredMessageVariable(currentMessage, objectName); 898 if (varMessage != null) { 899 String param = termElement.getAttributeValue("param"); 900 value = varMessage.getParameter(param); 901 } 902 } else if (termElement.getName().equals("winInfoValue")) { 903 String objectName = termElement.getAttributeValue("obj"); 904 varMessage = getStoredMessageVariable(currentMessage, objectName); 905 if (varMessage != null) { 906 String paramString = termElement.getAttributeValue("winParam"); 907 if (paramString.equals("class")) { 908 value = varMessage.getWindowClass(); 909 } else if (paramString.equals("resourceId")) { 910 value = "" + varMessage.getWindowResourceId(); 911 } else if (paramString.equals("hwnd")) { 912 value = "" + varMessage.getHwnd(); 913 } else if (paramString.equals("parentTarget")) { 914 String target = varMessage.getXmlWindowDescription(); 915 int index = target.lastIndexOf("<"); 916 if (index == 0) { 917 Console.traceln("Trying to adress parent of top-level window! Replay probably invalid!"); 918 } 919 value = target.substring(0, index); 920 } else if (paramString.equals("parentClass")) { 921 value = varMessage.getParentClass(); 922 } 923 } 924 } else if (termElement.getName().equals("msgInfoValue")) { 925 String objectName = termElement.getAttributeValue("obj"); 926 varMessage = getStoredMessageVariable(currentMessage, objectName); 927 if (varMessage != null) { 928 String paramString = termElement.getAttributeValue("msgParam"); 929 if (paramString.equals("type")) { 930 value = "" + varMessage.getType(); 931 } else if (paramString.equals("target")) { 932 value = varMessage.getXmlWindowDescription(); 933 } 934 } 935 } 936 return value; 937 } 938 939 /** 940 * <p> 941 * Handles term-nodes contained by equalSeq nodes. 942 * </p> 943 * 944 * @param termElement 945 * {@link Element} representing the term-node 946 * @return list of values of the term 947 */ 948 private List<String> getTermValueSeq(Element termElement) { 949 List<String> values = new LinkedList<String>(); 950 if (termElement.getName().equals("seqValue")) { 951 String obj = termElement.getAttributeValue("seqObj"); 952 String param = termElement.getAttributeValue("param"); 953 List<WindowsMessage> seqVar = getStoredSeqVariable(obj); 954 955 for (WindowsMessage msg : seqVar) { 956 // msg.getParameter returns null, if parameter is not found, 957 // therefore the List can contain null-values 958 values.add(msg.getParameter(param)); 959 } 960 } 961 return values; 962 } 963 964 /** 965 * <p> 966 * Handles LOWORD and HIWORD child nodes of LPARAM and WPARAM nodes. The 967 * returned value is the LPARAM/WPARAM value based on the LOWORD and HIWORD. 968 * </p> 969 * 970 * @param param 971 * {@link Element} representing the LPARAM/WPARAM node 972 * @return value of the LPARAM/WPARAM 973 */ 974 private long loHiWord(Element param) { 975 Element loword = param.getChild("LOWORD", rulesNamespace); 976 Element hiword = param.getChild("HIWORD", rulesNamespace); 977 String lowordStr = getTermValue(null, (Element) loword.getChildren() 978 .get(0)); 979 String hiwordStr = getTermValue(null, (Element) hiword.getChildren() 980 .get(0)); 981 return MAKEPARAM(Short.parseShort(lowordStr), 982 Short.parseShort(hiwordStr)); 983 } 984 985 /** 986 * <p> 987 * Takes to short integers and combines them into the high and low order 988 * bits of an integer. 989 * </p> 990 * 991 * @param loword 992 * low word 993 * @param hiword 994 * high word 995 * @return combined integer 996 */ 997 private static int MAKEPARAM(short loword, short hiword) { 998 return loword | ((int) hiword) << Short.SIZE; 999 } 44 /** 45 * <p> 46 * the list of all event generation rules available 47 * </p> 48 */ 49 private List<EventGenerationRule> generationRules; 50 51 /** 52 * <p> 53 * Name and path of the XML files containing the rules. 54 * </p> 55 */ 56 private String rulesFile; 57 58 /** 59 * <p> 60 * Iterator used for the current sequence. 61 * </p> 62 */ 63 private ListIterator<WindowsMessage> sequenceIterator; 64 65 /** 66 * <p> 67 * Token that is currently being generated. 68 * </p> 69 */ 70 private MFCEvent currentEvent; 71 72 /** 73 * <p> 74 * Event type of the current token. Stored as a member to be able to update it during the 75 * parsing of the idinfo tag. 76 * </p> 77 */ 78 private IEventType currentType; 79 80 /** 81 * <p> 82 * 83 * </p> 84 */ 85 private MFCGUIElement currentTarget; 86 87 /** 88 * <p> 89 * Reference to the ul:rules namespace. 90 * </p> 91 */ 92 private static Namespace rulesNamespace; 93 94 /** 95 * <p> 96 * The name of the rule that is currently being evaluated. 97 * </p> 98 */ 99 private String currentRuleName; 100 101 /** 102 * <p> 103 * Internal message storage. Used to implement the <code>{@literal <store>}</code> and 104 * <code>{@literal <storeSeq>}</code> tags. 105 * </p> 106 */ 107 private Map<String, Object> messageStorage; 108 109 /** 110 * <p> 111 * reference to the window tree created during parsing 112 * </p> 113 */ 114 private WindowTree windowTree; 115 116 /** 117 * <p> 118 * Creates a new EventGenerator. Sets "data/rules.xml" as default file for the rules. 119 * </p> 120 */ 121 public EventGenerator(WindowTree windowTree) { 122 rulesFile = "data/rules.xml"; 123 this.windowTree = windowTree; 124 } 125 126 /** 127 * <p> 128 * Tries to match the rules to the given sequence to generate an {@link WindowsEvent}. 129 * </p> 130 * <p> 131 * The rules are matched the order, in which they are defined in the XML file. Therefore, the 132 * order of the rules in the file defines priorities, when multiple rules could be matched to 133 * the same sequence. 134 * </p> 135 * 136 * @param sequence 137 * sequence of message for which an event will be generated 138 * @return event that matches the messages; null, if no rule can be matched 139 */ 140 public Event generateEvent(List<WindowsMessage> sequence) { 141 if (generationRules == null) { 142 parseGenerationRules(); 143 } 144 145 146 /*System.out.println("generating event for "); 147 for (WindowsMessage msg : sequence) { 148 System.out.println(" " + msg + " " + msg.getParameter("scrollBarHandle") + " " + msg.getTargetXML()); 149 }*/ 150 151 boolean isMatch = false; 152 153 for (int ruleIndex = 0; ruleIndex < generationRules.size() && !isMatch; ruleIndex++) { 154 EventGenerationRule currentRule = generationRules.get(ruleIndex); 155 currentRuleName = currentRule.getName(); 156 157 //System.out.println("checking rule " + currentRuleName); 158 159 messageStorage = new HashMap<String, Object>(); 160 sequenceIterator = sequence.listIterator(); 161 162 isMatch = evaluateMessageConditions(currentRule); 163 164 if (isMatch) { 165 currentType = MFCEventTypeFactory.getInstance().getEventType 166 (currentRuleName, resolveParameters(currentRule.getEventParameters())); 167 168 currentEvent = new MFCEvent(currentType, currentTarget, windowTree.getGUIModel()); 169 170 for (EventGenerationRule.ReplayMessageSpec replayMessageSpec : 171 currentRule.getReplayMessageSpecifications()) 172 { 173 if (replayMessageSpec.generateSingleMessage()) { 174 try { 175 generateReplayMessage(replayMessageSpec); 176 } 177 catch (InvalidParameterException e) { 178 Console.printerrln(e.getMessage()); 179 // TODO currentToken.invalidateReplay(); 180 } 181 } 182 else { 183 try { 184 generateReplaySequence(replayMessageSpec); 185 // TODO currentToken.invalidateReplay(); 186 } 187 catch (InvalidParameterException e) { 188 Console.printerrln(e.getMessage()); 189 // TODO currentToken.invalidateReplay(); 190 } 191 } 192 } 193 194 Console.traceln(currentEvent.getType().toString() + " matched"); 195 } 196 else { 197 currentEvent = null; 198 } 199 } 200 if (!isMatch) { 201 Console.traceln("no match found for sequence: " + sequence.toString()); 202 } 203 204 205 /*if (currentEvent != null && currentEvent.getReplayables() != null) { 206 System.out.println("replay messages are "); 207 for (de.ugoe.cs.quest.eventcore.IReplayable msg : currentEvent.getReplayables()) { 208 System.out.println(" " + msg + " " + msg.getReplay()); 209 } 210 } 211 212 System.out.println();*/ 213 214 215 return currentEvent; 216 } 217 218 // //////////////////////////////////////////////////////////// 219 // Helper functions for matching of events, i.e., msg-nodes // 220 // //////////////////////////////////////////////////////////// 221 222 /** 223 * <p> 224 * TODO: comment 225 * </p> 226 * 227 * @param currentRule 228 */ 229 private boolean evaluateMessageConditions(EventGenerationRule currentRule) { 230 boolean isMatch = true; 231 List<EventGenerationRule.MessageCondition> messageConditions = 232 currentRule.getMessageConditions(); 233 234 int i = 0; 235 while (isMatch && i < messageConditions.size()) { 236 EventGenerationRule.MessageCondition messageCondition = messageConditions.get(i); 237 if (messageCondition.matchMultiple()) { 238 EventGenerationRule.MessageCondition nextMessageCondition = null; 239 if (i + 1 < messageConditions.size()) { 240 nextMessageCondition = messageConditions.get(i + 1); 241 } 242 try { 243 isMatch = matchMultipleConditions(messageCondition, nextMessageCondition); 244 } 245 catch (InvalidParameterException e) { 246 Console.printerrln(e.getMessage()); 247 } 248 } 249 else { 250 try { 251 isMatch = matchSingleMessage(messageCondition); 252 } 253 catch (InvalidParameterException e) { 254 Console.printerrln(e.getMessage()); 255 } 256 } 257 i++; 258 } 259 260 return isMatch; 261 } 262 263 /** 264 * <p> 265 * Handles msg-nodes where multiple is not true, i.e., not a sequences. 266 * </p> 267 * 268 * @param messageElement 269 * {@link Element} representing the msg-node 270 * @return true, if a match is found; false otherwise 271 */ 272 private boolean matchSingleMessage(EventGenerationRule.MessageCondition condition) { 273 boolean isMatch = false; 274 WindowsMessage currentMessage = null; 275 276 WindowsMessageType type = condition.getMessageType(); 277 278 while (!isMatch && sequenceIterator.hasNext()) { 279 /* 280 * traverses the messages from the current position forward till a message with the 281 * correct type is found 282 */ 283 currentMessage = sequenceIterator.next(); 284 if (type == currentMessage.getType()) { 285 // message with the correct type found 286 // eval child nodes for further matching/storing 287 isMatch = evaluateMessageCondition(currentMessage, condition); 288 289 // in case the message is a match, eval storage children 290 if (isMatch) { 291 handleStorage(condition, currentMessage); 292 currentTarget = currentMessage.getTarget(); 293 // TODO currentToken.setTargetShort(currentMessage.getParentNames()); 294 } 295 } 296 } 297 298 return isMatch; 299 } 300 301 /** 302 * <p> 303 * Handles msg-nodes where multiple is true, i.e., sequences. Requires knowledge about the next 304 * msg-node to determine the end of the sequence. 305 * </p> 306 * 307 * @param messageElement 308 * {@link Element} representing the msg-node 309 * @param nextMessageElement 310 * {@link Element} representing the next msg-node; {@code null} if the current node 311 * is the last one 312 * @return true, if a sequence is matched; false otherwise 313 */ 314 private boolean matchMultipleConditions(EventGenerationRule.MessageCondition condition, 315 EventGenerationRule.MessageCondition nextCondition) 316 { 317 boolean isMatch = false; 318 boolean isCurrentMatch = false; 319 boolean nextMatchFound = false; 320 WindowsMessage currentMessage = null; 321 WindowsMessage nextMessage = null; 322 323 WindowsMessageType type = condition.getMessageType(); 324 325 WindowsMessageType nextType = null; 326 if (nextCondition != null) { 327 nextType = nextCondition.getMessageType(); 328 } 329 330 while (!nextMatchFound && sequenceIterator.hasNext()) { 331 currentMessage = sequenceIterator.next(); 332 if (type == currentMessage.getType()) { 333 isCurrentMatch = evaluateMessageCondition(currentMessage, condition); 334 isMatch = isMatch || isCurrentMatch; 335 336 if (isCurrentMatch) { 337 handleStorage(condition, currentMessage); 338 currentTarget = currentMessage.getTarget(); 339 // TODO currentToken.setTargetShort(currentMessage.getParentNames()); 340 } 341 } 342 if (nextCondition != null && isMatch) { 343 // peek next message to check if the sequence ends and the next 344 // match is found 345 if (!sequenceIterator.hasNext()) { 346 return false; // sequence is over, but not all messages are 347 // found 348 } 349 nextMessage = sequenceIterator.next(); 350 sequenceIterator.previous(); 351 352 if (nextType == nextMessage.getType()) { 353 nextMatchFound = evaluateMessageCondition(nextMessage, nextCondition); 354 } 355 356 } 357 } 358 359 return isMatch; 360 } 361 362 /** 363 * <p> 364 * Handles equals-nodes. 365 * </p> 366 * 367 * @param currentMessage 368 * {@link Element} representing the msg-node the equals-node belongs to 369 * @param messageElement 370 * {@link Element} representing the equals-node to be evaluated 371 * @return true, if constraint is fulfilled; false otherwise 372 */ 373 private boolean evaluateMessageCondition(WindowsMessage currentMessage, 374 EventGenerationRule.MessageCondition condition) 375 { 376 boolean isMatch = true; 377 for (int i = 0; isMatch && (i < condition.getAttributeConditions().size()); i++) 378 { 379 EventGenerationRule.AttributeCondition attrCond = 380 condition.getAttributeConditions().get(i); 381 382 // the size 2 of termElements is guaranteed by the XML schema 383 Object value1 = getTermValue(currentMessage, attrCond.getLeftHandSide(), Object.class); 384 Object value2 = getTermValue(currentMessage, attrCond.getRightHandSide(), Object.class); 385 if (value1 == null || value2 == null) { 386 isMatch = false; 387 } 388 else { 389 isMatch = isMatch && value1.equals(value2); 390 } 391 } 392 // for (Element childElement : (List<Element>) messageElement.getChildren("equalsSeq", 393 // rulesNamespace)) 394 // { 395 // List<Element> termElements = childElement.getChildren(); 396 // List<String> values1 = getTermValueSeq(termElements.get(0)); 397 // List<String> values2 = getTermValueSeq(termElements.get(0)); 398 // if (values1 == null || values2 == null) { 399 // isMatch = false; 400 // } 401 // else { 402 // isMatch = isMatch && values1.equals(values2); 403 // } 404 // } 405 return isMatch; 406 } 407 408 /** 409 * <p> 410 * Handles store-nodes and storeSeq-nodes. 411 * </p> 412 * 413 * @param messageElement 414 * {@link Element} representing the msg-node that is currently being evaluated 415 * @param currentMessage 416 * current message in the message sequence that is matched; this is the message that 417 * is stored 418 */ 419 @SuppressWarnings("unchecked") 420 private void handleStorage(EventGenerationRule.MessageCondition messageCondition, 421 WindowsMessage currentMessage) 422 { 423 for (Term valueToStore : messageCondition.getMessagesToStore()) 424 { 425 if (valueToStore.getMessageId() != null) { 426 ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(currentMessage); 427 messageStorage.put(valueToStore.getMessageId(), replayMessage); 428 resolveHwnd(replayMessage, valueToStore.getResolveHandles()); 429 } 430 else if (valueToStore.getSequenceId() != null) { 431 Object tmp = messageStorage.get(valueToStore.getSequenceId()); 432 ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(currentMessage); 433 List<ReplayWindowsMessage> storedSequence; 434 if (tmp == null || tmp instanceof ReplayWindowsMessage) { 435 storedSequence = new LinkedList<ReplayWindowsMessage>(); 436 storedSequence.add(replayMessage); 437 messageStorage.put(valueToStore.getSequenceId(), storedSequence); 438 } 439 else if (tmp instanceof List<?>) { 440 storedSequence = (List<ReplayWindowsMessage>) tmp; 441 storedSequence.add(replayMessage); 442 messageStorage.put(valueToStore.getSequenceId(), storedSequence); 443 } 444 resolveHwnd(replayMessage, valueToStore.getResolveHandles()); 445 } 446 } 447 } 448 449 /** 450 * <p> 451 * Resolves a parameter that contains a HWND of a message to the target string of the HWND and 452 * stores it. 453 * </p> 454 * 455 * @param currentMessage 456 * message whose HWND is resolved 457 * @param list 458 * child element of the store node that represents the resolve 459 */ 460 private void resolveHwnd(ReplayWindowsMessage currentMessage, List<Term> resolveHandles) { 461 if (resolveHandles != null) { 462 for (Term resolveElement : resolveHandles) { 463 String param = resolveElement.getMessageParameterName(); 464 String storeParam = resolveElement.getStoreParameterName(); 465 long paramHwnd = (Long) currentMessage.getParameter(param); 466 MFCGUIElement guiElement = windowTree.find(paramHwnd); 467 if (guiElement != null) { 468 currentMessage.addParameter(storeParam, "" + guiElement.toXML()); 469 } 470 } 471 } 472 } 473 474 // ///////////////////////////////////////////////////// 475 // Helper functions for generating the replay, i.e., 476 // parsing of genMsg und genMsgSeq-nodes 477 // ///////////////////////////////////////////////////// 478 479 /** 480 * <p> 481 * Handles genMsg-nodes and adds the replay to the {@link Event} that is generated. 482 * </p> 483 * 484 * @param genMsgElement 485 * {@link Element} representing the genMsg-node 486 */ 487 private void generateReplayMessage(EventGenerationRule.ReplayMessageSpec messageSpec) { 488 ReplayWindowsMessage generatedMessage = null; 489 if (messageSpec.getReplayObjectId() != null) { // replay stored message without change 490 generatedMessage = getStoredMessageVariable(null, messageSpec.getReplayObjectId()); 491 } 492 else { // generate message according to the rule 493 generatedMessage = new ReplayWindowsMessage 494 (getTermValue(messageSpec.getType(), WindowsMessageType.class)); 495 generatedMessage.setTargetXML(getTermValue(messageSpec.getTarget(), String.class)); 496 497 if ((messageSpec.getLparamHiWord() != null) || 498 (messageSpec.getLparamLoWord() != null)) 499 { 500 generatedMessage.setLPARAM 501 (loHiWord(messageSpec.getLparamLoWord(), messageSpec.getLparamHiWord())); 502 } 503 else if (messageSpec.getLparam() != null) { 504 try { 505 generatedMessage.setLPARAM(getTermValue(messageSpec.getLparam(), Long.class)); 506 } 507 catch (IllegalArgumentException e) { 508 generatedMessage.setLPARAMasWindowDesc 509 (getTermValue(messageSpec.getLparam(), String.class)); 510 } 511 } 512 513 if ((messageSpec.getWparamHiWord() != null) || 514 (messageSpec.getWparamLoWord() != null)) 515 { 516 generatedMessage.setWPARAM 517 (loHiWord(messageSpec.getWparamLoWord(), messageSpec.getWparamHiWord())); 518 } 519 else if (messageSpec.getWparam() != null) { 520 try { 521 generatedMessage.setWPARAM(getTermValue(messageSpec.getWparam(), Long.class)); 522 } 523 catch (IllegalArgumentException e) { 524 generatedMessage.setWPARAMasWindowDesc 525 (getTermValue(messageSpec.getWparam(), String.class)); 526 } 527 } 528 529 530 } 531 532 generatedMessage.setDelay(messageSpec.getDelay()); 533 534 currentEvent.addReplayable(generatedMessage); 535 } 536 537 /** 538 * Handles genMsgSeq-nodes and adds the replay to the {@link Event} that is generated.</p> 539 * 540 * @param genMsgElement 541 * {@link Element} representing the genMsgSeq-node. 542 */ 543 private void generateReplaySequence(EventGenerationRule.ReplayMessageSpec messageSpec) 544 { 545 List<ReplayWindowsMessage> generatedMessageSeq = new LinkedList<ReplayWindowsMessage>(); 546 if (messageSpec.getReplayObjectId() != null) { // replay stored sequence without changes 547 generatedMessageSeq = getStoredSeqVariable(messageSpec.getReplayObjectId()); 548 } 549 else { 550 List<ReplayWindowsMessage> seqVar = 551 getStoredSeqVariable(messageSpec.getReplayObjectId()); 552 553 Term typeTerm = messageSpec.getType(); 554 if (typeTerm.getSequenceId() != null) { 555 seqVar = getStoredSeqVariable(typeTerm.getSequenceId()); 556 for (WindowsMessage msg : seqVar) { 557 ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(msg.getType()); 558 replayMessage.setDelay(messageSpec.getDelay()); 559 generatedMessageSeq.add(replayMessage); 560 } 561 } 562 else { // constValue type 563 WindowsMessageType constMsgType = getTermValue(typeTerm, WindowsMessageType.class); 564 for (int i = 0; i < seqVar.size(); i++) { 565 ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(constMsgType); 566 replayMessage.setDelay(messageSpec.getDelay()); 567 generatedMessageSeq.add(replayMessage); 568 } 569 } 570 571 createSequenceTarget(generatedMessageSeq, messageSpec); 572 createSequenceLParam(generatedMessageSeq, messageSpec); 573 createSequenceWParam(generatedMessageSeq, messageSpec); 574 } 575 576 currentEvent.addReplayableSequence(generatedMessageSeq); 577 } 578 579 /** 580 * <p> 581 * Creates the targets for replay sequences generated with genMsgSeq-nodes. 582 * </p> 583 * 584 * @param generatedMessageSeq 585 * list of the messages that is being generated 586 * @param msgsGenerated 587 * boolean stating if the list of messages is already generated or if the generation 588 * has to be handles by this method 589 * @param constMsgType 590 * a constant message type that is used for message generation, in case the list of 591 * message is generated by this method 592 * @param termElement 593 * {@link Element} representing the term-node describing the target 594 * @return true, if the list of message is generated after calling this method; false otherwise 595 * @throws NoSuchElementException 596 * thrown if the seqVar referred to in the termElement contains a different number 597 * of messages than is contained in messageSeq 598 */ 599 private void createSequenceTarget(List<ReplayWindowsMessage> generatedMessageSeq, 600 EventGenerationRule.ReplayMessageSpec messageSpec) 601 throws NoSuchElementException 602 { 603 Iterator<ReplayWindowsMessage> seqIterator = generatedMessageSeq.iterator(); 604 if (messageSpec.getTarget().getSequenceId() != null) { 605 List<ReplayWindowsMessage> seqVar = 606 getStoredSeqVariable(messageSpec.getTarget().getSequenceId()); 607 608 if (seqVar.size() != generatedMessageSeq.size()) { 609 throw new InvalidParameterException 610 ("Failure generating replay sequence for rule " + currentRuleName + 611 ": One or more of the sequence variables used to generate a sequence have " + 612 "different lenghts."); 613 } 614 615 for (WindowsMessage msg : seqVar) { 616 seqIterator.next().setTarget(msg.getTarget()); 617 } 618 } 619 else { // const value 620 throw new AssertionError("target must be a sequence variable!"); 621 /* 622 * If target would not be a variable, the message-elements could not yet be created and 623 * the whole sequence might be broken. If this is to be changed, createSequenceLParam 624 * and createSequenceWParam need to be addepted, too. 625 */ 626 } 627 } 628 629 /** 630 * <p> 631 * Creates the LPARAMs for replay sequences generated with genMsgSeq-nodes. 632 * </p> 633 * 634 * @param generatedMessageSeq 635 * list of the messages that is being generated 636 * @param msgsGenerated 637 * boolean stating if the list of messages is already generated or if the generation 638 * has to be handles by this method 639 * @param constMsgType 640 * a constant message type that is used for message generation, in case the list of 641 * message is generated by this method 642 * @param termElement 643 * {@link Element} representing the term-node describing the LPARAM 644 * @return true, if the list of message is generated after calling this method; false otherwise 645 * @throws NoSuchElementException 646 * thrown if the seqVar referred to in the termElement contains a different number 647 * of messages than is contained in messageSeq 648 */ 649 private void createSequenceLParam(List<ReplayWindowsMessage> generatedMessageSeq, 650 EventGenerationRule.ReplayMessageSpec messageSpec) 651 throws NoSuchElementException 652 { 653 Iterator<ReplayWindowsMessage> seqIterator = generatedMessageSeq.iterator(); 654 if (messageSpec.getLparam().getSequenceId() != null) { 655 List<ReplayWindowsMessage> seqVar = 656 getStoredSeqVariable(messageSpec.getLparam().getSequenceId()); 657 658 if (seqVar.size() != generatedMessageSeq.size()) { 659 throw new InvalidParameterException 660 ("Failure generating replay sequence for rule " + currentRuleName + 661 ": One or more of the sequence variables used to generate a sequence have " + 662 "different lengths."); 663 } 664 for (WindowsMessage msg : seqVar) { 665 ReplayWindowsMessage currentSeqMsg = seqIterator.next(); 666 Object paramValue = 667 msg.getParameter(messageSpec.getLparam().getSequenceParameterName()); 668 if (paramValue instanceof Long) { 669 currentSeqMsg.setLPARAM((Long) paramValue); 670 } 671 else { 672 currentSeqMsg.setLPARAMasWindowDesc((String) paramValue); 673 } 674 } 675 } 676 else { // const value 677 int paramValue = getTermValue(messageSpec.getLparam(), int.class); 678 while (seqIterator.hasNext()) { 679 seqIterator.next().setLPARAM(paramValue); 680 } 681 } 682 683 } 684 685 /** 686 * <p> 687 * Creates the WPARAMs for replay sequences generated with genMsgSeq-nodes. 688 * </p> 689 * 690 * @param generatedMessageSeq 691 * list of the messages that is being generated 692 * @param msgsGenerated 693 * boolean stating if the list of messages is already generated or if the generation 694 * has to be handles by this method 695 * @param constMsgType 696 * a constant message type that is used for message generation, in case the list of 697 * message is generated by this method 698 * @param termElement 699 * {@link Element} representing the term-node describing the WPARAM 700 * @return true, if the list of message is generated after calling this method; false otherwise 701 * @throws NoSuchElementException 702 * thrown if the seqVar referred to in the termElement contains a different number 703 * of messages than is contained in messageSeq 704 */ 705 private void createSequenceWParam(List<ReplayWindowsMessage> generatedMessageSeq, 706 EventGenerationRule.ReplayMessageSpec messageSpec) 707 throws NoSuchElementException 708 { 709 Iterator<ReplayWindowsMessage> seqIterator = generatedMessageSeq.iterator(); 710 if (messageSpec.getWparam().getSequenceId() != null) { 711 List<ReplayWindowsMessage> seqVar = 712 getStoredSeqVariable(messageSpec.getWparam().getSequenceId()); 713 714 if (seqVar.size() != generatedMessageSeq.size()) { 715 throw new InvalidParameterException 716 ("Failure generating replay sequence for rule " + currentRuleName + 717 ": One or more of the sequence variables used to generate a sequence have " + 718 "different lengths."); 719 } 720 for (WindowsMessage msg : seqVar) { 721 ReplayWindowsMessage currentSeqMsg = seqIterator.next(); 722 Object paramValue = 723 msg.getParameter(messageSpec.getWparam().getSequenceParameterName()); 724 if (paramValue instanceof Long) { 725 currentSeqMsg.setWPARAM((Long) paramValue); 726 } 727 else { 728 currentSeqMsg.setWPARAMasWindowDesc((String) paramValue); 729 } 730 } 731 } 732 else { // const value 733 int paramValue = getTermValue(messageSpec.getWparam(), int.class); 734 while (seqIterator.hasNext()) { 735 seqIterator.next().setWPARAM(paramValue); 736 } 737 } 738 } 739 740 // //////////////////////////// 741 // General helper functions // 742 // //////////////////////////// 743 744 /** 745 * <p> 746 * TODO: comment 747 * </p> 748 * 749 * @param eventParameters 750 * @return 751 */ 752 private Map<String, String> resolveParameters(List<Term> eventParameters) { 753 Map<String, String> resultParameters = null; 754 755 if ((eventParameters != null) && (eventParameters.size() > 0)) { 756 resultParameters = new HashMap<String, String>(); 757 758 for (Term term : eventParameters) { 759 if ("seqValue".equals(term.getName())) { 760 List<String> values = getTermValueAsList(term, String.class); 761 762 resultParameters.put 763 (term.getSequenceParameterName(), (String) values.get(values.size() - 1)); 764 } 765 else { 766 resultParameters.put 767 (term.getMessageParameterName(), getTermValue(term, String.class)); 768 } 769 } 770 } 771 772 return resultParameters; 773 } 774 775 /** 776 * <p> 777 * Retrieves a message from the storage for, e.g., comparison or replay. "this" is used to refer 778 * to the current message. 779 * </p> 780 * 781 * @param currentMessage 782 * current message during the parsing; passed to handle "this" 783 * @param obj 784 * object identifier in the storage 785 * @return message retrieved from the storage 786 * @throws InvalidParameterException 787 * thrown in case of invalid uses of "this" or if no message with the identifier obj 788 * is found in the storage 789 */ 790 private ReplayWindowsMessage getStoredMessageVariable(WindowsMessage currentMessage, String obj) 791 throws InvalidParameterException 792 { 793 ReplayWindowsMessage varMessage = null; 794 if (obj.equals("this")) { 795 if (currentMessage == null) { 796 throw new InvalidParameterException("Failure obtaining term value for rule " + 797 currentRuleName + 798 ": \"this\" is not a valid name for generating runtime messages."); 799 } 800 varMessage = new ReplayWindowsMessage(currentMessage); 801 } 802 else { 803 Object tmp = messageStorage.get(obj); 804 if (tmp instanceof ReplayWindowsMessage) { 805 varMessage = (ReplayWindowsMessage) tmp; 806 } 807 else { 808 throw new InvalidParameterException("Failure obtaining term value for rule " + 809 currentRuleName + ": No message \"" + obj + "\" stored."); 810 } 811 } 812 return varMessage; 813 } 814 815 /** 816 * <p> 817 * Retrieves a stored message sequence from the storage. 818 * </p> 819 * 820 * @param obj 821 * object identifier in the storage 822 * @return message sequence retrieved from the storage 823 * @throws IllegalArgumentException 824 * thrown if no message sequences with the identifier obj is found in the storage 825 */ 826 @SuppressWarnings("unchecked") 827 private List<ReplayWindowsMessage> getStoredSeqVariable(String obj) 828 throws IllegalArgumentException 829 { 830 List<ReplayWindowsMessage> varMsgSeq = null; 831 Object tmp = messageStorage.get(obj); 832 if (tmp instanceof List<?>) { 833 varMsgSeq = (List<ReplayWindowsMessage>) tmp; 834 } 835 else { 836 throw new IllegalArgumentException("Failure obtaining term value for rule " + 837 currentRuleName + ": No sequence \"" + obj + 838 "\" store."); 839 } 840 return varMsgSeq; 841 } 842 843 /** 844 * <p> 845 * convenience method for {@link #getTermValue(WindowsMessage, Term)} with current message is 846 * null. 847 * </p> 848 * 849 * @param termElement 850 * {@link Element} representing the term node 851 * @return value of the term or {@code null} of the term node could not be evaluated 852 */ 853 private <T> T getTermValue(EventGenerationRule.Term term, Class<T> expectedType) { 854 return getTermValue(null, term, expectedType); 855 } 856 857 /** 858 * <p> 859 * Handles term-nodes and returns the value of the described term. 860 * </p> 861 * 862 * @param currentMessage 863 * current message during the parsing; required to resolve references to "this" in a 864 * term 865 * @param termElement 866 * {@link Element} representing the term node 867 * @return value of the term or {@code null} of the term node could not be evaluated 868 */ 869 private <T> T getTermValue(WindowsMessage currentMessage, 870 EventGenerationRule.Term term, 871 Class<T> expectedType) 872 { 873 T value = null; 874 875 if ("constValue".equals(term.getName())) { 876 value = getValueAsType(term.getValue(), expectedType); 877 } 878 else if ("paramValue".equals(term.getName())) { 879 String objectName = term.getMessageId(); 880 WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName); 881 if (varMessage != null) { 882 String param = term.getMessageParameterName(); 883 value = getValueAsType(varMessage.getParameter(param), expectedType); 884 } 885 } 886 else if ("winInfoValue".equals(term.getName())) { 887 String objectName = term.getMessageId(); 888 WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName); 889 if (varMessage != null) { 890 String paramString = term.getWindowParameterName(); 891 if (paramString.equals("class")) { 892 value = getValueAsType 893 (((MFCGUIElement) varMessage.getTarget()).getType(), expectedType); 894 } 895 else if (paramString.equals("resourceId")) { 896 value = getValueAsType 897 (((MFCGUIElement) varMessage.getTarget()).getResourceId(), expectedType); 898 } 899 else if (paramString.equals("hwnd")) { 900 value = getValueAsType 901 (((MFCGUIElement) varMessage.getTarget()).getId(), expectedType); 902 } 903 else if (paramString.equals("parentTarget")) { 904 String target = varMessage.getTargetXML(); 905 int index = target.lastIndexOf("<"); 906 if (index == 0) { 907 Console.traceln("Trying to adress parent of top-level window! Replay " + 908 "probably invalid!"); 909 } 910 value = getValueAsType(target.substring(0, index), expectedType); 911 } 912 else if (paramString.equals("parentClass")) { 913 value = getValueAsType 914 (((MFCGUIElement) varMessage.getTarget()) 915 .getParent().getSpecification().getType(), expectedType); 916 } 917 } 918 } 919 else if ("msgInfoValue".equals(term.getName())) { 920 String objectName = term.getMessageId(); 921 WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName); 922 if (varMessage != null) { 923 String paramString = term.getMessageInfoName(); 924 if (paramString.equals("type")) { 925 value = getValueAsType(varMessage.getType(), expectedType); 926 } 927 else if (paramString.equals("target")) { 928 value = getValueAsType(varMessage.getTargetXML(), expectedType); 929 } 930 } 931 } 932 else if ("msgInfoValue".equals(term.getName())) { 933 String objectName = term.getMessageId(); 934 WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName); 935 if (varMessage != null) { 936 String paramString = term.getMessageInfoName(); 937 if (paramString.equals("type")) { 938 value = getValueAsType(varMessage.getType(), expectedType); 939 } 940 else if (paramString.equals("target")) { 941 value = getValueAsType(varMessage.getTargetXML(), expectedType); 942 } 943 } 944 } 945 946 return value; 947 } 948 949 /** 950 * <p> 951 * convenience method for {@link #getTermValueAsList(WindowsMessage, Term)} with current 952 * message is null. 953 * </p> 954 * 955 * @param termElement 956 * {@link Element} representing the term node 957 * @return value of the term or {@code null} of the term node could not be evaluated 958 */ 959 private <T> List<T> getTermValueAsList(EventGenerationRule.Term term, Class<T> expectedType) { 960 return getTermValueAsList(null, term, expectedType); 961 } 962 963 /** 964 * <p> 965 * Handles term-nodes and returns the value of the described term. 966 * </p> 967 * 968 * @param currentMessage 969 * current message during the parsing; required to resolve references to "this" in a 970 * term 971 * @param termElement 972 * {@link Element} representing the term node 973 * @return value of the term or {@code null} of the term node could not be evaluated 974 */ 975 private <T> List<T> getTermValueAsList(WindowsMessage currentMessage, 976 EventGenerationRule.Term term, 977 Class<T> expectedElementType) 978 { 979 List<T> values = new ArrayList<T>(); 980 if ("seqValue".equals(term.getName())) { 981 List<ReplayWindowsMessage> seqVar = getStoredSeqVariable(term.getSequenceId()); 982 Object value; 983 984 for (ReplayWindowsMessage msg : seqVar) { 985 // msg.getParameter returns null, if parameter is not found, 986 // therefore the List can contain null-values 987 value = msg.getParameter(term.getSequenceParameterName()); 988 values.add(getValueAsType(value, expectedElementType)); 989 } 990 } 991 else { 992 values.add(getTermValue(currentMessage, term, expectedElementType)); 993 } 994 995 return values; 996 } 997 998 /** 999 * <p> 1000 * TODO: comment 1001 * </p> 1002 * 1003 * @param value 1004 * @param expectedType 1005 * @return 1006 */ 1007 @SuppressWarnings("unchecked") 1008 private <T> T getValueAsType(Object value, Class<T> expectedType) { 1009 if (expectedType.isInstance(value)) { 1010 return (T) value; 1011 } 1012 else if (value instanceof String) { 1013 try { 1014 if (WindowsMessageType.class.equals(expectedType)) { 1015 return (T) WindowsMessageType.parseMessageType((String) value); 1016 } 1017 else if (Short.class.equals(expectedType)) { 1018 return (T) (Short) Short.parseShort((String) value); 1019 } 1020 } 1021 catch (Exception e) { 1022 // in this case, the value can not be transformed to the expected value. So ignore 1023 // the exception and fall through to the exception thrown anyway 1024 } 1025 } 1026 else if (value instanceof Long) { 1027 try { 1028 if (Short.class.equals(expectedType)) { 1029 return (T) (Short) ((Long) value).shortValue(); 1030 } 1031 else if (String.class.equals(expectedType)) { 1032 return (T) ((Long) value).toString(); 1033 } 1034 } 1035 catch (Exception e) { 1036 // in this case, the value can not be transformed to the expected value. So ignore 1037 // the exception and fall through to the exception thrown anyway 1038 } 1039 } 1040 1041 throw new IllegalArgumentException("the term value is not of the expected type " + 1042 expectedType + " but a " + 1043 (value != null ? value.getClass() : "null")); 1044 } 1045 1046 /** 1047 * <p> 1048 * Handles LOWORD and HIWORD child nodes of LPARAM and WPARAM nodes. The returned value is the 1049 * LPARAM/WPARAM value based on the LOWORD and HIWORD. 1050 * </p> 1051 * 1052 * @param param 1053 * {@link Element} representing the LPARAM/WPARAM node 1054 * @return value of the LPARAM/WPARAM 1055 */ 1056 private long loHiWord(EventGenerationRule.Term lword, EventGenerationRule.Term hword) { 1057 return MAKEPARAM(getTermValue(lword, Short.class), getTermValue(hword, Short.class)); 1058 } 1059 1060 /** 1061 * <p> 1062 * Takes to short integers and combines them into the high and low order bits of an integer. 1063 * </p> 1064 * 1065 * @param loword 1066 * low word 1067 * @param hiword 1068 * high word 1069 * @return combined integer 1070 */ 1071 private static int MAKEPARAM(short loword, short hiword) { 1072 return loword | ((int) hiword) << Short.SIZE; 1073 } 1074 1075 /** 1076 * <p> 1077 * TODO: comment 1078 * </p> 1079 * 1080 */ 1081 @SuppressWarnings("unchecked") 1082 private void parseGenerationRules() { 1083 SAXBuilder builder = new SAXBuilder(); 1084 Document doc = null; 1085 1086 try { 1087 doc = builder.build(rulesFile); 1088 rulesNamespace = Namespace.getNamespace("ul:rules"); 1089 } 1090 catch (JDOMException e) { 1091 Console.printerrln("Invalid rules file."); 1092 e.printStackTrace(); 1093 } 1094 catch (IOException e) { 1095 Console.printerrln("Invalid rules file."); 1096 e.printStackTrace(); 1097 } 1098 1099 Element rulesRoot = doc.getRootElement(); 1100 1101 List<Element> ruleElements = rulesRoot.getChildren("rule", rulesNamespace); 1102 1103 generationRules = new ArrayList<EventGenerationRule>(); 1104 1105 for (Element ruleElement : ruleElements) { 1106 generationRules.add(new EventGenerationRule(ruleElement, rulesNamespace)); 1107 } 1108 } 1000 1109 1001 1110 } -
trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/HandlerCreate.java
r581 r619 1 1 2 package de.ugoe.cs.quest.plugin.mfc; 2 3 3 import de.ugoe.cs.quest.plugin.mfc. eventcore.WindowTree;4 import de.ugoe.cs.quest.plugin.mfc.guimodel.WindowTree; 4 5 5 6 /** 6 7 * <p> 7 * Message handler for {@code WM_CREATE} messages. The handler maintains the 8 * {@link WindowTree}. 8 * Message handler for {@code WM_CREATE} messages. The handler maintains the {@link WindowTree}. 9 9 * </p> 10 10 * … … 14 14 public class HandlerCreate extends MessageHandler { 15 15 16 /** 17 * <p> 18 * Constructor. Creates a new HandlerCreate. 19 * </p> 20 */ 21 public HandlerCreate() { 22 super(); 23 } 16 /** 17 * <p> 18 * Constructor. Creates a new HandlerCreate. 19 * </p> 20 * 21 * @param windowTree the tree of GUI element specifications to be created and adapted during 22 * parsing 23 */ 24 public HandlerCreate(WindowTree windowTree) { 25 super(windowTree); 26 } 24 27 25 26 27 28 29 30 28 /** 29 * <p> 30 * Name of the created window. 31 * </p> 32 */ 33 private String windowName; 31 34 32 33 34 35 36 37 private inthwnd;35 /** 36 * <p> 37 * HWND of the created window. 38 * </p> 39 */ 40 private long hwnd; 38 41 39 40 41 42 43 44 private intparentHwnd;42 /** 43 * <p> 44 * HWND of the created window's parent. 45 * </p> 46 */ 47 private long parentHwnd; 45 48 46 47 48 49 50 51 49 /** 50 * <p> 51 * Resource Id of the created window. 52 * </p> 53 */ 54 private int resourceId; 52 55 53 54 55 56 57 58 56 /** 57 * <p> 58 * Window class of the created window. 59 * </p> 60 */ 61 private String className; 59 62 60 61 62 63 64 65 63 /** 64 * <p> 65 * Modality of the created window. 66 * </p> 67 */ 68 private boolean isModal; 66 69 67 /* 68 * (non-Javadoc) 69 * 70 * @see de.ugoe.cs.quest.plugin.mfc.MessageHandler#onEndElement() 71 */ 72 @Override 73 public void onEndElement() { 74 if (hwnd != 0) { 75 WindowTree.getInstance().add(parentHwnd, hwnd, windowName, 76 resourceId, className, isModal); 77 } 78 } 70 /* 71 * (non-Javadoc) 72 * 73 * @see de.ugoe.cs.quest.plugin.mfc.MessageHandler#onEndElement() 74 */ 75 @Override 76 public void onEndElement() { 77 if (hwnd != 0) { 78 super.getWindowTree().add(parentHwnd, hwnd, windowName, resourceId, className, isModal); 79 } 80 } 79 81 80 /* 81 * (non-Javadoc) 82 * 83 * @see 84 * de.ugoe.cs.quest.plugin.mfc.MessageHandler#onParameter(java.lang.String 85 * , java.lang.String) 86 */ 87 @Override 88 public void onParameter(String name, String value) { 89 if (name.equals("window.hwnd")) { 90 hwnd = Integer.parseInt(value); 91 } else if (name.equals("window.name")) { 92 windowName = value; 93 } else if (name.equals("window.parent.hwnd")) { 94 parentHwnd = Integer.parseInt(value); 95 } else if (name.equals("window.resourceId")) { 96 resourceId = Integer.parseInt(value); 97 } else if (name.equals("window.class")) { 98 if (value.startsWith("Afx:")) { 99 className = "Afx:"; 100 } else { 101 className = value; 102 } 103 } else if (name.equals("window.ismodal")) { 104 if (value.equals("true") || value.equals("1")) { 105 isModal = true; 106 } 107 } 108 } 82 /* 83 * (non-Javadoc) 84 * 85 * @see de.ugoe.cs.quest.plugin.mfc.MessageHandler#onParameter(java.lang.String , 86 * java.lang.String) 87 */ 88 @Override 89 public void onParameter(String name, String value) { 90 if (name.equals("window.hwnd")) { 91 hwnd = Long.parseLong(value); 92 } 93 else if (name.equals("window.name")) { 94 windowName = value; 95 } 96 else if (name.equals("window.parent.hwnd")) { 97 parentHwnd = Long.parseLong(value); 98 } 99 else if (name.equals("window.resourceId")) { 100 resourceId = Integer.parseInt(value); 101 } 102 else if (name.equals("window.class")) { 103 if (value.startsWith("Afx:")) { 104 className = "Afx:"; 105 } 106 else { 107 className = value; 108 } 109 } 110 else if (name.equals("window.ismodal")) { 111 if (value.equals("true") || value.equals("1")) { 112 isModal = true; 113 } 114 } 115 } 109 116 110 111 112 113 114 115 116 117 118 119 120 121 122 123 117 /* 118 * (non-Javadoc) 119 * 120 * @see de.ugoe.cs.quest.plugin.mfc.MessageHandler#onStartElement() 121 */ 122 @Override 123 public void onStartElement() { 124 windowName = ""; 125 hwnd = 0; 126 parentHwnd = 0; 127 resourceId = 0; 128 className = ""; 129 isModal = false; 130 } 124 131 125 132 } -
trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/HandlerDestroy.java
r581 r619 1 1 2 package de.ugoe.cs.quest.plugin.mfc; 2 3 3 import de.ugoe.cs.quest.plugin.mfc. eventcore.WindowTree;4 import de.ugoe.cs.quest.plugin.mfc.guimodel.WindowTree; 4 5 5 6 /** 6 7 * <p> 7 * Handler for {@code WM_DESTROY} message. The handler maintains the 8 * {@link WindowTree}. 8 * Handler for {@code WM_DESTROY} message. The handler maintains the {@link WindowTree}. 9 9 * </p> 10 10 * … … 14 14 public class HandlerDestroy extends MessageHandler { 15 15 16 /** 17 * <p> 18 * Constructor. Creates a new HandlerDestroy. 19 * </p> 20 */ 21 public HandlerDestroy() { 22 super(); 23 } 16 /** 17 * <p> 18 * Constructor. Creates a new HandlerDestroy. 19 * </p> 20 * 21 * @param windowTree 22 * the tree of GUI element specifications to be created and adapted during parsing 23 */ 24 public HandlerDestroy(WindowTree windowTree) { 25 super(windowTree); 26 } 24 27 25 26 27 28 29 30 private inthwnd;28 /** 29 * <p> 30 * HWND of the window that is destroyed. 31 * </p> 32 */ 33 private long hwnd; 31 34 32 33 34 35 36 37 38 39 40 WindowTree.getInstance().remove(hwnd);41 42 35 /* 36 * (non-Javadoc) 37 * 38 * @see de.ugoe.cs.quest.plugin.mfc.MessageHandler#onEndElement() 39 */ 40 @Override 41 public void onEndElement() { 42 if (hwnd != 0) { 43 super.getWindowTree().remove(hwnd); 44 } 45 } 43 46 44 /* 45 * (non-Javadoc) 46 * 47 * @see 48 * de.ugoe.cs.quest.plugin.mfc.MessageHandler#onParameter(java.lang.String 49 * , java.lang.String) 50 */ 51 @Override 52 public void onParameter(String name, String value) { 53 if (name.equals("window.hwnd")) { 54 hwnd = Integer.parseInt(value); 55 } 56 } 47 /* 48 * (non-Javadoc) 49 * 50 * @see de.ugoe.cs.quest.plugin.mfc.MessageHandler#onParameter(java.lang.String , 51 * java.lang.String) 52 */ 53 @Override 54 public void onParameter(String name, String value) { 55 if (name.equals("window.hwnd")) { 56 hwnd = Long.parseLong(value); 57 } 58 } 57 59 58 59 60 61 62 63 64 65 66 60 /* 61 * (non-Javadoc) 62 * 63 * @see de.ugoe.cs.quest.plugin.mfc.MessageHandler#onStartElement() 64 */ 65 @Override 66 public void onStartElement() { 67 hwnd = 0; 68 } 67 69 68 70 } -
trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/HandlerSetText.java
r581 r619 1 1 2 package de.ugoe.cs.quest.plugin.mfc; 2 3 3 import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowTree; 4 import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowTreeNode; 4 import de.ugoe.cs.quest.plugin.mfc.guimodel.WindowTree; 5 5 6 6 /** … … 14 14 public class HandlerSetText extends MessageHandler { 15 15 16 /** 17 * <p> 18 * Constructor. Creates a new HanderSetText. 19 * </p> 20 */ 21 public HandlerSetText() { 22 super(); 23 } 16 /** 17 * <p> 18 * Constructor. Creates a new HanderSetText. 19 * </p> 20 * 21 * @param windowTree 22 * the tree of GUI element specifications to be created and adapted during parsing 23 */ 24 public HandlerSetText(WindowTree windowTree) { 25 super(windowTree); 26 } 24 27 25 26 27 28 29 30 28 /** 29 * <p> 30 * New name of the window. 31 * </p> 32 */ 33 private String windowName; 31 34 32 33 34 35 36 37 private inthwnd;35 /** 36 * <p> 37 * HWND of the window. 38 * </p> 39 */ 40 private long hwnd; 38 41 39 /* 40 * (non-Javadoc) 41 * 42 * @see de.ugoe.cs.quest.plugin.mfc.MessageHandler#onEndElement() 43 */ 44 @Override 45 public void onEndElement() { 46 if (hwnd != 0) { 47 WindowTreeNode node = WindowTree.getInstance().find(hwnd); 48 node.setName(windowName); 49 } 50 } 42 /* 43 * (non-Javadoc) 44 * 45 * @see de.ugoe.cs.quest.plugin.mfc.MessageHandler#onEndElement() 46 */ 47 @Override 48 public void onEndElement() { 49 if (hwnd != 0) { 50 super.getWindowTree().setName(hwnd, windowName); 51 } 52 } 51 53 52 53 54 55 * @see 56 * de.ugoe.cs.quest.plugin.mfc.MessageHandler#onParameter(java.lang.String 57 * , java.lang.String) 58 */ 59 @Override 60 public void onParameter(String name, String value) {61 if (name.equals("window.hwnd")) { 62 hwnd = Integer.parseInt(value); 63 }else if (name.equals("window.newText")) {64 65 66 54 /* 55 * (non-Javadoc) 56 * 57 * @see de.ugoe.cs.quest.plugin.mfc.MessageHandler#onParameter(java.lang.String , 58 * java.lang.String) 59 */ 60 @Override 61 public void onParameter(String name, String value) { 62 if (name.equals("window.hwnd")) { 63 hwnd = Long.parseLong(value); 64 } 65 else if (name.equals("window.newText")) { 66 windowName = value; 67 } 68 } 67 69 68 69 70 71 72 73 74 75 76 77 70 /* 71 * (non-Javadoc) 72 * 73 * @see de.ugoe.cs.quest.plugin.mfc.MessageHandler#onStartElement() 74 */ 75 @Override 76 public void onStartElement() { 77 windowName = ""; 78 hwnd = 0; 79 } 78 80 } -
trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/MFCLogParser.java
r581 r619 1 1 2 package de.ugoe.cs.quest.plugin.mfc; 2 3 … … 8 9 import java.security.InvalidParameterException; 9 10 import java.util.Collection; 11 import java.util.HashMap; 10 12 import java.util.LinkedList; 11 13 import java.util.List; 14 import java.util.Map; 12 15 import java.util.SortedMap; 13 16 import java.util.TreeMap; … … 24 27 25 28 import de.ugoe.cs.quest.eventcore.Event; 26 import de.ugoe.cs.quest. plugin.mfc.eventcore.WindowTree;29 import de.ugoe.cs.quest.eventcore.guimodel.GUIModel; 27 30 import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessage; 31 import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessageType; 32 import de.ugoe.cs.quest.plugin.mfc.guimodel.MFCGUIElement; 33 import de.ugoe.cs.quest.plugin.mfc.guimodel.WindowTree; 28 34 import de.ugoe.cs.util.StringTools; 29 35 import de.ugoe.cs.util.console.Console; … … 31 37 /** 32 38 * <p> 33 * This class provides functionality to parse XML log files generated by the 34 * MFCUsageMonitor of EventBench. The result of parsing a file is a collection 35 * of event sequences. It uses the {@link SequenceSplitter} and the 36 * {@link EventGenerator} as well as custom defined {@link MessageHandler} for 37 * the parsing. 39 * This class provides functionality to parse XML log files generated by the MFCUsageMonitor of 40 * EventBench. The result of parsing a file is a collection of event sequences. It uses the 41 * {@link SequenceSplitter} and the {@link EventGenerator} as well as custom defined 42 * {@link MessageHandler} for the parsing. 38 43 * </p> 39 44 * … … 43 48 public class MFCLogParser extends DefaultHandler { 44 49 45 /** 46 * <p> 47 * If a custom message handler is used, this field contains its handle. 48 * Otherwise this field is {@code null}. 49 * </p> 50 */ 51 private MessageHandler currentHandler; 52 53 /** 54 * <p> 55 * Handle to the message that is currently parsed. 56 * </p> 57 */ 58 private WindowsMessage currentMessage; 59 60 /** 61 * <p> 62 * {@link SequenceSplitter} instance used by the {@link MFCLogParser}. 63 * </p> 64 */ 65 private SequenceSplitter sequenceSplitter; 66 67 /** 68 * <p> 69 * Collection of event sequences that is contained in the log file, which is 70 * parsed. 71 * </p> 72 */ 73 private Collection<List<Event>> sequences; 74 75 /** 76 * <p> 77 * Debugging variable that allows the analysis which message type occurs how 78 * often in the log file. Can be used to enhance the message filter. 79 * </p> 80 */ 81 private SortedMap<Integer, Integer> typeCounter; 82 83 /** 84 * <p> 85 * Debugging variable that enables the counting of the occurrences of each 86 * message. Used in combination with {@link #typeCounter}. 87 * </p> 88 */ 89 private boolean countMessageOccurences; 90 91 /** 92 * <p> 93 * Constructor. Creates a new LogParser that does not count message 94 * occurrences. 95 * </p> 96 */ 97 public MFCLogParser() { 98 this(false); 99 } 100 101 /** 102 * <p> 103 * Constructor. Creates a new LogParser. 104 * </p> 105 * 106 * @param countMessageOccurences 107 * if true, the occurrences of each message type in the log is 108 * counted. 109 */ 110 public MFCLogParser(boolean countMessageOccurences) { 111 sequenceSplitter = new SequenceSplitter(); 112 sequences = new LinkedList<List<Event>>(); 113 currentHandler = null; 114 this.countMessageOccurences = countMessageOccurences; 115 if (countMessageOccurences) { 116 typeCounter = new TreeMap<Integer, Integer>(); 117 } 118 119 } 120 121 /** 122 * <p> 123 * Returns the collection of event sequences that is obtained from parsing 124 * log files. 125 * </p> 126 * 127 * @return collection of event sequences 128 */ 129 public Collection<List<Event>> getSequences() { 130 return sequences; 131 } 132 133 /* 134 * (non-Javadoc) 135 * 136 * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, 137 * java.lang.String, java.lang.String, org.xml.sax.Attributes) 138 */ 139 @Override 140 public void startElement(String uri, String localName, String qName, 141 Attributes atts) throws SAXException { 142 if (qName.equals("session")) { 143 Console.traceln("start of session"); 144 sequenceSplitter = new SequenceSplitter(); 145 } else if (qName.equals("msg")) { 146 String msgType = atts.getValue("type"); 147 int msgInt = -1; 148 try { 149 msgInt = Integer.parseInt(msgType); 150 151 if (countMessageOccurences) { 152 Integer currentCount = typeCounter.get(msgInt); 153 if (currentCount == null) { 154 typeCounter.put(msgInt, 1); 155 } else { 156 typeCounter.put(msgInt, currentCount + 1); 157 } 158 } 159 160 if (msgInt == MessageDefs.WM_CREATE) { 161 currentHandler = new HandlerCreate(); 162 currentHandler.onStartElement(); 163 } else if (msgInt == MessageDefs.WM_DESTROY) { 164 currentHandler = new HandlerDestroy(); 165 currentHandler.onStartElement(); 166 } else if (msgInt == MessageDefs.WM_SETTEXT) { 167 currentHandler = new HandlerSetText(); 168 currentHandler.onStartElement(); 169 } else { 170 currentMessage = new WindowsMessage(msgInt); 171 } 172 } catch (NumberFormatException e) { 173 Console.printerrln("Invalid message type: type not a number"); 174 e.printStackTrace(); 175 } 176 } else if (qName.equals("param")) { 177 if (currentHandler != null) { 178 currentHandler.onParameter(atts.getValue("name"), 179 atts.getValue("value")); 180 } else { 181 currentMessage.addParameter(atts.getValue("name"), 182 atts.getValue("value")); 183 } 184 } 185 } 186 187 /* 188 * (non-Javadoc) 189 * 190 * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, 191 * java.lang.String, java.lang.String) 192 */ 193 @Override 194 public void endElement(String uri, String localName, String qName) 195 throws SAXException { 196 if (qName.equals("msg")) { 197 if (currentHandler != null) { 198 currentHandler.onEndElement(); 199 currentHandler = null; 200 } else { 201 try { 202 currentMessage.setTarget(WindowTree.getInstance()); 203 sequenceSplitter.addMessage(currentMessage); 204 } catch (InvalidParameterException e) { 205 Console.traceln(e.getMessage() + " WindowsMessage " 206 + currentMessage + " ignored."); 207 } 208 } 209 } else if (qName.equals("session")) { 210 sequenceSplitter.endSession(); 211 List<Event> seq = sequenceSplitter.getSequence(); 212 if( seq!=null && !seq.isEmpty() ) { 213 sequences.add(seq); 214 } 215 Console.traceln("end of session"); 216 } 217 } 218 219 /** 220 * <p> 221 * Parses a given log file created by the MFCMonitor and adds its contents 222 * to the collection of event sequences. 223 * </p> 224 * 225 * @param filename 226 * name and path of the log file 227 */ 228 public void parseFile(String filename) { 229 if (filename == null) { 230 throw new InvalidParameterException("filename must not be null"); 231 } 232 233 SAXParserFactory spf = SAXParserFactory.newInstance(); 234 spf.setValidating(true); 235 236 SAXParser saxParser = null; 237 InputSource inputSource = null; 238 try { 239 saxParser = spf.newSAXParser(); 240 inputSource = new InputSource(new InputStreamReader( 241 new FileInputStream(filename)));//, "UTF-8")); 242 //} catch (UnsupportedEncodingException e) { 243 // e.printStackTrace(); 244 } catch (ParserConfigurationException e) { 245 e.printStackTrace(); 246 } catch (SAXException e) { 247 e.printStackTrace(); 248 } catch (FileNotFoundException e) { 249 e.printStackTrace(); 250 } 251 if (inputSource != null) { 252 inputSource.setSystemId("file://" 253 + new File(filename).getAbsolutePath()); 254 try { 255 if (saxParser == null) { 256 throw new RuntimeException("SAXParser creation failed"); 257 } 258 saxParser.parse(inputSource, this); 259 } catch (SAXParseException e) { 260 Console.printerrln("Failure parsing file in line " 261 + e.getLineNumber() + ", column " + e.getColumnNumber() 262 + "."); 263 e.printStackTrace(); 264 } catch (SAXException e) { 265 e.printStackTrace(); 266 } catch (IOException e) { 267 e.printStackTrace(); 268 } 269 } 270 if (countMessageOccurences) { 271 Console.println("Message statistics:"); 272 Console.println(typeCounter.toString() 273 .replace(" ", StringTools.ENDLINE) 274 .replaceAll("[\\{\\}]", "")); 275 } 276 } 50 /** 51 * <p> 52 * If a custom message handler is used, this field contains its handle. Otherwise this field is 53 * {@code null}. 54 * </p> 55 */ 56 private MessageHandler currentHandler; 57 58 /** 59 * <p> 60 * internal handle to the current window tree 61 * </p> 62 */ 63 private WindowTree currentWindowTree; 64 65 /** 66 * <p> 67 * the type of the currently parsed message 68 * </p> 69 */ 70 private WindowsMessageType currentMessageType; 71 72 /** 73 * <p> 74 * the parameters of the currently parsed message 75 * </p> 76 */ 77 private Map<String, Object> currentMessageParameters = new HashMap<String, Object>(); 78 79 /** 80 * <p> 81 * {@link SequenceSplitter} instance used by the {@link MFCLogParser}. 82 * </p> 83 */ 84 private SequenceSplitter sequenceSplitter; 85 86 /** 87 * <p> 88 * Collection of message sequences that is contained in the log file, which is parsed. 89 * </p> 90 */ 91 private Collection<List<Event>> sequences; 92 93 /** 94 * <p> 95 * Debugging variable that allows the analysis which message type occurs how often in the log 96 * file. Can be used to enhance the message filter. 97 * </p> 98 */ 99 private SortedMap<WindowsMessageType, Integer> typeCounter; 100 101 /** 102 * <p> 103 * Debugging variable that enables the counting of the occurrences of each message. Used in 104 * combination with {@link #typeCounter}. 105 * </p> 106 */ 107 private boolean countMessageOccurences; 108 109 /** 110 * <p> 111 * Constructor. Creates a new LogParser that does not count message occurrences. 112 * </p> 113 */ 114 public MFCLogParser() { 115 this(false); 116 } 117 118 /** 119 * <p> 120 * Constructor. Creates a new LogParser. 121 * </p> 122 * 123 * @param countMessageOccurences 124 * if true, the occurrences of each message type in the log is counted. 125 */ 126 public MFCLogParser(boolean countMessageOccurences) { 127 sequences = new LinkedList<List<Event>>(); 128 currentHandler = null; 129 this.countMessageOccurences = countMessageOccurences; 130 if (countMessageOccurences) { 131 typeCounter = new TreeMap<WindowsMessageType, Integer>(); 132 } 133 } 134 135 /** 136 * <p> 137 * Parses a log file written by the MFCMonitor and creates a collection of event sequences. 138 * </p> 139 * 140 * @param filename 141 * name and path of the log file 142 */ 143 public void parseFile(String filename) { 144 if (filename == null) { 145 throw new InvalidParameterException("filename must not be null"); 146 } 147 148 parseFile(new File(filename)); 149 } 150 151 /** 152 * <p> 153 * Parses a log file written by the MFCMonitor and creates a collection of event sequences. 154 * </p> 155 * 156 * @param file 157 * name and path of the log file 158 */ 159 public void parseFile(File file) { 160 if (file == null) { 161 throw new InvalidParameterException("file must not be null"); 162 } 163 164 SAXParserFactory spf = SAXParserFactory.newInstance(); 165 spf.setValidating(true); 166 167 SAXParser saxParser = null; 168 InputSource inputSource = null; 169 try { 170 saxParser = spf.newSAXParser(); 171 inputSource = new InputSource(new InputStreamReader(new FileInputStream(file)));// , 172 // "UTF-8")); 173 // } catch (UnsupportedEncodingException e) { 174 // e.printStackTrace(); 175 } 176 catch (ParserConfigurationException e) { 177 // TODO handle Exception 178 e.printStackTrace(); 179 } 180 catch (SAXException e) { 181 // TODO handle Exception 182 e.printStackTrace(); 183 } 184 catch (FileNotFoundException e) { 185 // TODO handle Exception 186 e.printStackTrace(); 187 } 188 189 if (inputSource != null) { 190 inputSource.setSystemId("file://" + file.getAbsolutePath()); 191 try { 192 if (saxParser == null) { 193 throw new RuntimeException("SAXParser creation failed"); 194 } 195 saxParser.parse(inputSource, this); 196 } 197 catch (SAXParseException e) { 198 Console.printerrln("Failure parsing file in line " + e.getLineNumber() + 199 ", column " + e.getColumnNumber() + "."); 200 e.printStackTrace(); 201 } 202 catch (SAXException e) { 203 // TODO handle Exception 204 e.printStackTrace(); 205 } 206 catch (IOException e) { 207 // TODO handle Exception 208 e.printStackTrace(); 209 } 210 } 211 212 if (countMessageOccurences) { 213 Console.println("Message statistics:"); 214 Console.println 215 (typeCounter.toString().replace(" ", StringTools.ENDLINE).replaceAll("[\\{\\}]", "")); 216 } 217 } 218 219 /** 220 * <p> 221 * Returns the collection of event sequences that is obtained from parsing log files. 222 * </p> 223 * 224 * @return collection of event sequences 225 */ 226 public Collection<List<Event>> getSequences() { 227 return sequences; 228 } 229 230 /** 231 * <p> 232 * Returns the gui model that is obtained from parsing log files. 233 * </p> 234 * 235 * @return collection of event sequences 236 */ 237 public GUIModel getGuiModel() { 238 return currentWindowTree.getGUIModel(); 239 } 240 241 /* 242 * (non-Javadoc) 243 * 244 * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, 245 * java.lang.String, org.xml.sax.Attributes) 246 */ 247 @Override 248 public void startElement(String uri, String localName, String qName, Attributes atts) 249 throws SAXException 250 { 251 if (qName.equals("session")) { 252 Console.traceln("start of session"); 253 // in some logs, the session end may be marked in between the log. This is because 254 // of thread problems. So instead of creating a new GUI model, preserve it. 255 if (currentWindowTree == null) { 256 currentWindowTree = new WindowTree(); 257 } 258 sequenceSplitter = new SequenceSplitter(currentWindowTree); 259 } 260 else if (qName.equals("msg")) { 261 currentMessageType = WindowsMessageType.parseMessageType(atts.getValue("type")); 262 263 if (countMessageOccurences) { 264 Integer currentCount = typeCounter.get(currentMessageType); 265 if (currentCount == null) { 266 typeCounter.put(currentMessageType, 1); 267 } 268 else { 269 typeCounter.put(currentMessageType, currentCount + 1); 270 } 271 } 272 273 if (currentMessageType == WindowsMessageType.WM_CREATE) { 274 currentHandler = new HandlerCreate(currentWindowTree); 275 currentHandler.onStartElement(); 276 } 277 else if (currentMessageType == WindowsMessageType.WM_DESTROY) { 278 currentHandler = new HandlerDestroy(currentWindowTree); 279 currentHandler.onStartElement(); 280 } 281 else if (currentMessageType == WindowsMessageType.WM_SETTEXT) { 282 currentHandler = new HandlerSetText(currentWindowTree); 283 currentHandler.onStartElement(); 284 } 285 } 286 else if (qName.equals("param")) { 287 if (currentHandler != null) { 288 currentHandler.onParameter(atts.getValue("name"), atts.getValue("value")); 289 } 290 else { 291 // provide the parameters directly in the correct type 292 String paramName = atts.getValue("name"); 293 if (("window.hwnd".equals(paramName)) || 294 ("source".equals(paramName)) || 295 ("LPARAM".equals(paramName)) || 296 ("WPARAM".equals(paramName)) || 297 ("scrollPos".equals(paramName)) || 298 ("scrollBarHandle".equals(paramName))) 299 { 300 Long paramValue = Long.parseLong(atts.getValue("value")); 301 currentMessageParameters.put(paramName, paramValue); 302 } 303 else { 304 currentMessageParameters.put(paramName, atts.getValue("value")); 305 } 306 } 307 } 308 } 309 310 /* 311 * (non-Javadoc) 312 * 313 * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, 314 * java.lang.String) 315 */ 316 @Override 317 public void endElement(String uri, String localName, String qName) throws SAXException { 318 if (qName.equals("msg")) { 319 if (currentHandler != null) { 320 currentHandler.onEndElement(); 321 currentHandler = null; 322 } 323 else { 324 try { 325 long hwnd = (Long) currentMessageParameters.get("window.hwnd"); 326 MFCGUIElement target = currentWindowTree.find(hwnd); 327 328 WindowsMessage message = new WindowsMessage 329 (currentMessageType, target, currentMessageParameters); 330 331 sequenceSplitter.addMessage(message); 332 } 333 catch (InvalidParameterException e) { 334 Console.traceln(e.getMessage() + " WindowsMessage " + currentMessageType + 335 " ignored."); 336 } 337 } 338 } 339 else if (qName.equals("session")) { 340 sequenceSplitter.endSession(); 341 List<Event> seq = sequenceSplitter.getSequence(); 342 if (seq != null && !seq.isEmpty()) { 343 sequences.add(seq); 344 } 345 Console.traceln("end of session"); 346 } 347 } 348 277 349 } -
trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/MessageHandler.java
r581 r619 1 1 2 package de.ugoe.cs.quest.plugin.mfc; 3 4 import de.ugoe.cs.quest.plugin.mfc.guimodel.WindowTree; 2 5 3 6 /** 4 7 * <p> 5 * Base class to define custom message handlers, for messages that shall be 6 * handled differently during the parsing of usage logs. It provides dummy 7 * implementations for all required methods, such that implementations can only 8 * overwrite the parts they actually require and ignore the rest. 8 * Base class to define custom message handlers, for messages that shall be handled differently 9 * during the parsing of usage logs. It provides dummy implementations for all required methods, 10 * such that implementations can only overwrite the parts they actually require and ignore the rest. 9 11 * </p> 10 12 * … … 14 16 public class MessageHandler { 15 17 16 /** 17 * <p> 18 * Constructor. Protected to prohibit initialization of the base class 19 * itself. 20 * </p> 21 */ 22 protected MessageHandler() { 23 } 18 /** 19 * <p> 20 * during parsing, a tree of GUI elements is created and adapted. 21 * This is the reference to it. 22 * </p> 23 */ 24 private WindowTree windowTree; 24 25 25 /** 26 * <p> 27 * Called in the startElement() method of the {@link MFCLogParser} when a 28 * msg-node begins. 29 * </p> 30 */ 31 public void onStartElement() { 32 } 26 /** 27 * <p> 28 * Constructor. Protected to prohibit initialization of the base class itself. 29 * </p> 30 * 31 * @param windowTree the tree of GUI element specifications to be created and adapted during 32 * parsing 33 */ 34 protected MessageHandler(WindowTree windowTree) { 35 this.windowTree = windowTree; 36 } 33 37 34 /** 35 * <p> 36 * Called by the {@link MFCLogParser} to handle param-nodes. 37 * </p> 38 * 39 * @param name 40 * name (type) of the parameter 41 * @param value 42 * value of the parameter 43 */ 44 public void onParameter(String name, String value) { 45 } 38 /** 39 * <p> 40 * Called in the startElement() method of the {@link MFCLogParser} when a msg-node begins. 41 * </p> 42 */ 43 public void onStartElement() {} 46 44 47 /** 48 * <p> 49 * Called in the endElement() method of {@link MFCLogParser} when a msg-node 50 * ends. 51 * </p> 52 */ 53 public void onEndElement() { 54 } 45 /** 46 * <p> 47 * Called by the {@link MFCLogParser} to handle param-nodes. 48 * </p> 49 * 50 * @param name 51 * name (type) of the parameter 52 * @param value 53 * value of the parameter 54 */ 55 public void onParameter(String name, String value) {} 56 57 /** 58 * <p> 59 * Called in the endElement() method of {@link MFCLogParser} when a msg-node ends. 60 * </p> 61 */ 62 public void onEndElement() {} 63 64 /** 65 * @return the window tree created and adapted during parsing 66 */ 67 protected WindowTree getWindowTree() { 68 return windowTree; 69 } 70 55 71 } -
trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/SequenceSplitter.java
r581 r619 1 1 2 package de.ugoe.cs.quest.plugin.mfc; 2 3 … … 6 7 import de.ugoe.cs.quest.eventcore.Event; 7 8 import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessage; 9 import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessageType; 10 import de.ugoe.cs.quest.plugin.mfc.guimodel.WindowTree; 8 11 import de.ugoe.cs.util.console.Console; 9 12 10 13 /** 11 14 * <p> 12 * Responsible to split sequences into subsequences, such that each subsequences 13 * contains exactlyone event.15 * Responsible to split sequences into subsequences, such that each subsequences contains exactly 16 * one event. 14 17 * </p> 15 18 * … … 19 22 public class SequenceSplitter { 20 23 21 22 23 24 25 26 24 /** 25 * <p> 26 * Contains the current subsequence. 27 * </p> 28 */ 29 private List<WindowsMessage> currentSequence; 27 30 28 29 30 * Number of messages in the current sequences, that signal that a key or 31 * mouse button has been pressed down to which not yet a messagehas been32 * found, that signals that the button has beenreleased.33 34 35 31 /** 32 * <p> 33 * Number of messages in the current sequences, that signal that a key or mouse button has been 34 * pressed down to which not yet a message has been found, that signals that the button has been 35 * released. 36 * </p> 37 */ 38 private int openDowns; 36 39 37 /** 38 * <p> 39 * Internal flag that signals if {@link #currentSequence} needs to be 40 * initialized. 41 * </p> 42 */ 43 private boolean initMessages; 40 /** 41 * <p> 42 * Internal flag that signals if {@link #currentSequence} needs to be initialized. 43 * </p> 44 */ 45 private boolean initMessages; 44 46 45 /** 46 * <p> 47 * The {@link EventGenerator} used to convert the subsequences into 48 * {@link Event}s 49 * </p> 50 */ 51 private EventGenerator tokenGenerator; 47 /** 48 * <p> 49 * The {@link EventGenerator} used to convert the subsequences into {@link Event}s 50 * </p> 51 */ 52 private EventGenerator tokenGenerator; 52 53 53 54 55 56 57 58 54 /** 55 * <p> 56 * The event sequence generated. 57 * </p> 58 */ 59 private List<Event> actionSequence; 59 60 60 61 62 63 64 65 private int prevMsg = 0;61 /** 62 * <p> 63 * Type of the previous message. 64 * </p> 65 */ 66 private WindowsMessageType prevMsg; 66 67 67 68 69 70 71 72 public SequenceSplitter() {73 74 75 76 tokenGenerator = new EventGenerator();77 78 prevMsg = 0;79 68 /** 69 * <p> 70 * Constructor. Creates a new SequenceSplitter. 71 * </p> 72 */ 73 public SequenceSplitter(WindowTree windowTree) { 74 currentSequence = new LinkedList<WindowsMessage>(); 75 openDowns = 0; 76 initMessages = true; 77 tokenGenerator = new EventGenerator(windowTree); 78 actionSequence = new LinkedList<Event>(); 79 prevMsg = null; 80 } 80 81 81 /** 82 * <p> 83 * Called by the {@link MFCLogParser} every time a message is parsed. 84 * </p> 85 * 86 * @param msg 87 * message to be added 88 */ 89 public void addMessage(WindowsMessage msg) { 90 if (startOfSequence(msg)) { 91 if (!initMessages) { 92 Event currentAction = tokenGenerator 93 .generateEvent(currentSequence); 94 if (currentAction != null) { 95 actionSequence.add(currentAction); 96 } 97 if (isKeyMessage(msg.getType()) && openDowns > 0) { 98 Console.traceln("Key message found with open down mouse messages - will probabably result in a faulty sequence."); 99 } 100 } else { 101 initMessages = false; 102 } 103 currentSequence = new LinkedList<WindowsMessage>(); 104 } 105 if (isUpMessage(msg.getType())) { 106 if (openDowns > 0) { 107 openDowns--; 108 } 109 } 82 /** 83 * <p> 84 * Called by the {@link MFCLogParser} every time a message is parsed. 85 * </p> 86 * 87 * @param msg 88 * message to be added 89 */ 90 public void addMessage(WindowsMessage msg) { 91 if (startOfSequence(msg)) { 92 if (!initMessages) { 93 Event currentAction = tokenGenerator.generateEvent(currentSequence); 94 if (currentAction != null) { 95 actionSequence.add(currentAction); 96 } 97 if (msg.getType().isKeyMessage() && openDowns > 0) { 98 Console 99 .traceln("Key message found with open down mouse messages - will probabably result in a faulty sequence."); 100 } 101 } 102 else { 103 initMessages = false; 104 } 105 currentSequence = new LinkedList<WindowsMessage>(); 106 } 107 if (msg.getType().isUpMessage()) { 108 if (openDowns > 0) { 109 openDowns--; 110 } 111 } 110 112 111 // this fix checks if there are two consecutive mouse-down messages. 112 // This sometimes occurs due to incorrect filtering in the monitoring 113 // dll. 114 if (!(prevMsg == MessageDefs.WM_LBUTTONDOWN && prevMsg == msg.getType())) { 115 currentSequence.add(msg); 116 } else { 117 openDowns--; 118 } 119 prevMsg = msg.getType(); 120 } 113 // this fix checks if there are two consecutive mouse-down messages. 114 // This sometimes occurs due to incorrect filtering in the monitoring 115 // dll. 116 if (!(prevMsg == WindowsMessageType.WM_LBUTTONDOWN && prevMsg == msg.getType())) { 117 currentSequence.add(msg); 118 } 119 else { 120 openDowns--; 121 } 122 prevMsg = msg.getType(); 123 } 121 124 122 /** 123 * <p> 124 * Returns the event sequence generated from the message that have been 125 * added. 126 * </p> 127 * 128 * @return generated event sequence 129 */ 130 public List<Event> getSequence() { 131 return actionSequence; 132 } 125 /** 126 * <p> 127 * Returns the event sequence generated from the message that have been added. 128 * </p> 129 * 130 * @return generated event sequence 131 */ 132 public List<Event> getSequence() { 133 return actionSequence; 134 } 133 135 134 /** 135 * <p> 136 * Called when a session in the log file is finished, i.e., a closing 137 * session-node is found. 138 * </p> 139 */ 140 public void endSession() { 141 Event currentAction = tokenGenerator 142 .generateEvent(currentSequence); 143 if (currentAction != null) { 144 actionSequence.add(currentAction); 145 } 146 } 136 /** 137 * <p> 138 * Called when a session in the log file is finished, i.e., a closing session-node is found. 139 * </p> 140 */ 141 public void endSession() { 142 Event currentAction = tokenGenerator.generateEvent(currentSequence); 143 if (currentAction != null) { 144 actionSequence.add(currentAction); 145 } 146 } 147 147 148 /** 149 * <p> 150 * Checks if the message starts a new subsequence and returns the result. 151 * </p> 152 * 153 * @param msg 154 * message that is checked 155 * @return true, if a new subsequence begins 156 */ 157 private boolean startOfSequence(WindowsMessage msg) { 158 boolean isStart = false; 159 int msgType = msg.getType(); 160 if (isKeyMessage(msgType)) { 161 isStart = true; 162 } 163 if (isDownMessage(msgType)) { 164 openDowns++; 165 if (openDowns == 1) { 166 isStart = true; 167 } 168 } 169 if (isDblclkMessage(msgType)) { 170 openDowns++; 171 } 172 return isStart; 173 } 174 175 /** 176 * <p> 177 * Checks if the type of a message is generated is a keyboard interaction. 178 * </p> 179 * 180 * @param msgType 181 * type of the message 182 * @return true if it is a keyboard interaction; false otherwise 183 */ 184 private boolean isKeyMessage(int msgType) { 185 boolean isKeyMsg = false; 186 switch (msgType) { 187 case MessageDefs.WM_KEYDOWN: 188 case MessageDefs.WM_KEYUP: 189 case MessageDefs.WM_SYSKEYDOWN: 190 case MessageDefs.WM_SYSKEYUP: 191 isKeyMsg = true; 192 break; 193 default: 194 break; 195 } 196 return isKeyMsg; 197 } 198 199 /** 200 * <p> 201 * Checks if the type of a message indicates that the mouse has been pressed 202 * down. 203 * </p> 204 * 205 * @param msgType 206 * type of the message 207 * @return true if it is mouse-down message; false otherwise 208 */ 209 private boolean isDownMessage(int msgType) { 210 boolean isDownMsg = false; 211 switch (msgType) { 212 case MessageDefs.WM_LBUTTONDOWN: 213 case MessageDefs.WM_RBUTTONDOWN: 214 case MessageDefs.WM_MBUTTONDOWN: 215 case MessageDefs.WM_XBUTTONDOWN: 216 case MessageDefs.WM_NCLBUTTONDOWN: 217 case MessageDefs.WM_NCRBUTTONDOWN: 218 case MessageDefs.WM_NCMBUTTONDOWN: 219 case MessageDefs.WM_NCXBUTTONDOWN: 220 isDownMsg = true; 221 break; 222 default: 223 break; 224 } 225 return isDownMsg; 226 } 227 228 /** 229 * <p> 230 * Checks if the type of a message indicates that a double click has been 231 * performed. 232 * </p> 233 * 234 * @param msgType 235 * type of the message 236 * @return true if it is a double click message; false otherwise 237 */ 238 private boolean isDblclkMessage(int msgType) { 239 boolean isDblclkMsg = false; 240 switch (msgType) { 241 case MessageDefs.WM_LBUTTONDBLCLK: 242 case MessageDefs.WM_RBUTTONDBLCLK: 243 case MessageDefs.WM_MBUTTONDBLCLK: 244 case MessageDefs.WM_XBUTTONDBLCLK: 245 case MessageDefs.WM_NCLBUTTONDBLCLK: 246 case MessageDefs.WM_NCRBUTTONDBLCLK: 247 case MessageDefs.WM_NCMBUTTONDBLCLK: 248 case MessageDefs.WM_NCXBUTTONDBLCLK: 249 isDblclkMsg = true; 250 break; 251 default: 252 break; 253 } 254 return isDblclkMsg; 255 } 256 257 /** 258 * <p> 259 * Checks if the type of a message indicates that the mouse has been 260 * released. 261 * </p> 262 * 263 * @param msgType 264 * type of the message 265 * @return true if it is mouse-up message; false otherwise 266 */ 267 private boolean isUpMessage(int msgType) { 268 boolean isUpMsg = false; 269 switch (msgType) { 270 case MessageDefs.WM_LBUTTONUP: 271 case MessageDefs.WM_RBUTTONUP: 272 case MessageDefs.WM_MBUTTONUP: 273 case MessageDefs.WM_XBUTTONUP: 274 case MessageDefs.WM_NCLBUTTONUP: 275 case MessageDefs.WM_NCRBUTTONUP: 276 case MessageDefs.WM_NCMBUTTONUP: 277 case MessageDefs.WM_NCXBUTTONUP: 278 isUpMsg = true; 279 break; 280 default: 281 break; 282 } 283 return isUpMsg; 284 } 148 /** 149 * <p> 150 * Checks if the message starts a new subsequence and returns the result. 151 * </p> 152 * 153 * @param msg 154 * message that is checked 155 * @return true, if a new subsequence begins 156 */ 157 private boolean startOfSequence(WindowsMessage msg) { 158 boolean isStart = false; 159 WindowsMessageType msgType = msg.getType(); 160 if (msgType.isKeyMessage()) { 161 isStart = true; 162 } 163 if (msgType.isDownMessage()) { 164 openDowns++; 165 if (openDowns == 1) { 166 isStart = true; 167 } 168 } 169 if (msgType.isDblclkMessage()) { 170 openDowns++; 171 } 172 return isStart; 173 } 285 174 286 175 } -
trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/commands/CMDparseXML.java
r566 r619 1 1 2 package de.ugoe.cs.quest.plugin.mfc.commands; 2 3 … … 4 5 import java.util.Collection; 5 6 import java.util.List; 6 import java.util.SortedSet;7 7 8 8 import de.ugoe.cs.quest.CommandHelpers; 9 9 import de.ugoe.cs.quest.eventcore.Event; 10 import de.ugoe.cs.quest.eventcore.guimodel.GUIModel; 10 11 import de.ugoe.cs.quest.plugin.mfc.MFCLogParser; 11 import de.ugoe.cs.quest.plugin.mfc.eventcore.MFCTargetComparator;12 import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowTree;13 12 import de.ugoe.cs.quest.ui.GlobalDataContainer; 14 13 import de.ugoe.cs.util.console.Command; … … 17 16 /** 18 17 * <p> 19 * Command to parse an XML file with sessions monitored by EventBench's 20 * MFCUsageMonitor. 18 * Command to parse an XML file with sessions monitored by EventBench's MFCUsageMonitor. 21 19 * </p> 22 20 * … … 26 24 public class CMDparseXML implements Command { 27 25 28 29 30 31 32 33 34 35 36 26 /* 27 * (non-Javadoc) 28 * 29 * @see de.ugoe.cs.util.console.Command#help() 30 */ 31 @Override 32 public void help() { 33 Console.println("Usage: parseXML <filename> {<sequencesName>} {<countMessageOccurences>}"); 34 } 37 35 38 39 40 41 42 43 44 45 46 47 36 /* 37 * (non-Javadoc) 38 * 39 * @see de.ugoe.cs.util.console.Command#run(java.util.List) 40 */ 41 @Override 42 public void run(List<Object> parameters) { 43 String filename; 44 String sequencesName = "sequences"; 45 boolean countMessageOccurences = false; 48 46 49 50 51 52 53 54 55 countMessageOccurences = Boolean 56 .parseBoolean((String) parameters.get(2)); 57 58 }catch (Exception e) {59 60 47 try { 48 filename = (String) parameters.get(0); 49 if (parameters.size() >= 2) { 50 sequencesName = (String) parameters.get(1); 51 } 52 if (parameters.size() >= 3) { 53 countMessageOccurences = Boolean.parseBoolean((String) parameters.get(2)); 54 } 55 } 56 catch (Exception e) { 57 throw new InvalidParameterException(); 58 } 61 59 62 63 60 MFCLogParser parser = new MFCLogParser(countMessageOccurences); 61 parser.parseFile(filename); 64 62 65 Collection<List<Event>> sequences = parser.getSequences(); 66 67 Console.traceln("Pre-computing event target equalities."); 68 // compare all Events to a dummy event to make sure they are known by 69 // the MFCTargetComparator 70 Event dummyEvent = Event.STARTEVENT; 71 for (List<Event> sequence : sequences) { 72 for (Event event : sequence) { 73 event.equals(dummyEvent); 74 } 75 } 76 MFCTargetComparator.setMutable(false); 77 78 SortedSet<String> targets = WindowTree.getInstance().getTargets(); 63 Collection<List<Event>> sequences = parser.getSequences(); 79 64 80 if (GlobalDataContainer.getInstance().addData(sequencesName, sequences)) { 81 CommandHelpers.dataOverwritten(sequencesName); 82 } 83 if (GlobalDataContainer.getInstance().addData( 84 sequencesName + "_targets", targets)) { 85 CommandHelpers.dataOverwritten(sequencesName + "_targets"); 86 } 87 } 65 GUIModel targets = parser.getGuiModel(); 66 67 if (GlobalDataContainer.getInstance().addData(sequencesName, sequences)) { 68 CommandHelpers.dataOverwritten(sequencesName); 69 } 70 if (GlobalDataContainer.getInstance().addData(sequencesName + "_targets", targets)) { 71 CommandHelpers.dataOverwritten(sequencesName + "_targets"); 72 } 73 } 88 74 89 75 } -
trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/eventcore/MFCEvent.java
r578 r619 1 1 2 package de.ugoe.cs.quest.plugin.mfc.eventcore; 2 3 3 import java.security.InvalidParameterException; 4 import java.util.HashMap; 5 import java.util.Map; 6 7 import de.ugoe.cs.quest.IReplayDecorator; 8 import de.ugoe.cs.quest.eventcore.IReplayable; 9 import de.ugoe.cs.quest.plugin.mfc.MFCReplayDecorator; 10 import de.ugoe.cs.util.StringTools; 4 import de.ugoe.cs.quest.eventcore.Event; 5 import de.ugoe.cs.quest.eventcore.IEventType; 6 import de.ugoe.cs.quest.eventcore.guimodel.GUIModel; 7 import de.ugoe.cs.quest.plugin.mfc.guimodel.MFCGUIElement; 11 8 12 9 /** 13 10 * <p> 14 * Contains all informations about a windows message, i.e., all parameters that 15 * are read when awindows message is parsed as well as its target, hwnd, etc.11 * Contains all informations about a windows message, i.e., all parameters that are read when a 12 * windows message is parsed as well as its target, hwnd, etc. 16 13 * </p> 17 14 * … … 20 17 * 21 18 */ 22 public class WindowsMessage implements IReplayable{19 public class MFCEvent extends Event { 23 20 24 25 26 27 28 29 21 /** 22 * <p> 23 * Id for object serialization. 24 * </p> 25 */ 26 private static final long serialVersionUID = 1L; 30 27 31 /** 32 * <p> 33 * Type of the message. 34 * </p> 35 */ 36 final int type; 28 /** 29 * <p> 30 * TODO: comment 31 * </p> 32 * 33 * @param eventType 34 * @param target 35 * @param currentMessageParameters 36 */ 37 public MFCEvent(IEventType eventType, 38 MFCGUIElement target, 39 GUIModel guiModel) 40 { 41 super(eventType); 42 super.setTarget(target); 43 } 37 44 38 /** 39 * <p> 40 * Window class of the message target. Default: "" 41 * </p> 42 */ 43 private String windowClass = ""; 44 45 /** 46 * <p> 47 * Resource Id of the message target. Default: 0 48 * </p> 49 */ 50 private int resourceId = 0; 51 52 /** 53 * <p> 54 * XML representation of the message target. 55 * </p> 56 */ 57 private String xmlWindowDescription = ""; 58 59 /** 60 * <p> 61 * String that contains the names of all parent widgets and itself, separated by dots, 62 * e.g., "GrandParent.Parent.self". 63 * </p> 64 */ 65 private String parentNames = null; 66 67 /** 68 * <p> 69 * String that contains the window class of the parent widget. 70 * </p> 71 */ 72 private String parentClass = null; 73 74 /** 75 * <p> 76 * LPARAM of the message. Default: 0 77 * </p> 78 */ 79 private long LPARAM = 0; 80 81 /** 82 * <p> 83 * WPARAM of the message. Default: 0 84 * </p> 85 */ 86 private long WPARAM = 0; 87 88 /** 89 * <p> 90 * If the LPARAM contains a HWND, this string stores the target of the HWND. 91 * </p> 92 */ 93 private String LPARAMasWindowDesc = null; 94 95 /** 96 * <p> 97 * If the WPARAM contains a HWND, this string stores the target of the HWND. 98 * </p> 99 */ 100 private String WPARAMasWindowDesc = null; 101 102 /** 103 * <p> 104 * Delay after sending the messages during a replay. Default: 0 105 * </p> 106 */ 107 private int delay = 0; 108 109 /** 110 * <p> 111 * A map of all parameters, associated with the message, created during the 112 * parsing of messages from the logs {@code param}-nodes. 113 * </p> 114 */ 115 private Map<String, String> params = new HashMap<String, String>(); 116 117 /** 118 * <p> 119 * Constructor. Creates a new message with a given message type. 120 * </p> 121 * 122 * @param type 123 * type of the message 124 */ 125 public WindowsMessage(int type) { 126 this.type = type; 127 } 128 129 /** 130 * <p> 131 * Adds a parameter to the message. 132 * </p> 133 * 134 * @param type 135 * type descriptor of the parameter 136 * @param value 137 * value of the parameter 138 */ 139 public void addParameter(String type, String value) { 140 params.put(type, value); 141 if (type.equals("LPARAM")) { 142 LPARAM = Long.parseLong(value); 143 } else if (type.equals("WPARAM")) { 144 WPARAM = Long.parseLong(value); 145 } 146 } 147 148 /** 149 * <p> 150 * Returns the type of the message. 151 * </p> 152 * 153 * @return type of the message 154 */ 155 public int getType() { 156 return type; 157 } 158 159 /** 160 * <p> 161 * Returns the value of a parameter, given its type. If the parameter is not 162 * found, {@code null} is returned. 163 * </p> 164 * 165 * @param type 166 * type of the parameter 167 * @return value of the parameter 168 */ 169 public String getParameter(String type) { 170 return params.get(type); 171 } 172 173 /** 174 * <p> 175 * Returns the window class of the message target. 176 * </p> 177 * 178 * @return window class of the message target 179 */ 180 public String getWindowClass() { 181 return windowClass; 182 } 183 184 /** 185 * <p> 186 * Returns the HWND the message is addressed to. 187 * </p> 188 * 189 * @return HWND the message is addressed to 190 */ 191 public int getHwnd() { 192 int hwnd = -1; 193 String hwndString = getParameter("window.hwnd"); // possible, as 194 // "window.hwnd" is 195 // mandatory 196 if (hwndString != null) { 197 hwnd = Integer.parseInt(hwndString); 198 } 199 return hwnd; 200 } 201 202 /** 203 * <p> 204 * Returns the resource Id of the message target. 205 * </p> 206 * 207 * @return resource Id of the message target 208 */ 209 public int getWindowResourceId() { 210 return resourceId; 211 } 212 213 /** 214 * <p> 215 * Two {@link WindowsMessage} are equal, if their {@link #type}, 216 * {@link #xmlWindowDescription}, and {@link #params} are equal. 217 * </p> 218 * 219 * @see java.lang.Object#equals(java.lang.Object) 220 */ 221 @Override 222 public boolean equals(Object other) { 223 if (other == this) { 224 return true; 225 } 226 boolean isEqual = false; 227 if (other instanceof WindowsMessage) { 228 isEqual = ((WindowsMessage) other).type == this.type 229 && ((WindowsMessage) other).xmlWindowDescription 230 .equals(this.xmlWindowDescription) 231 && ((WindowsMessage) other).params.equals(this.params); 232 } 233 return isEqual; 234 } 235 236 /* 237 * (non-Javadoc) 238 * 239 * @see java.lang.Object#hashCode() 240 */ 241 @Override 242 public int hashCode() { 243 int multiplier = 17; 244 int hash = 42; 245 246 hash = multiplier * hash + type; 247 hash = multiplier * hash + xmlWindowDescription.hashCode(); 248 hash = multiplier * hash + params.hashCode(); 249 250 return hash; 251 } 252 253 /** 254 * <p> 255 * Returns a string representation of the message of the form 256 * "msg[target=HWND;type=TYPE]". 257 * </p> 258 * 259 * @see java.lang.Object#toString() 260 */ 261 @Override 262 public String toString() { 263 return "msg[target=" + getParameter("window.hwnd") + ";type=" + type 264 + "]"; 265 } 266 267 /** 268 * <p> 269 * Retrieves the target string of a message from a given {@link WindowTree} 270 * through looking up the HWND the message is addressed to in the window 271 * tree. 272 * </p> 273 * 274 * @param windowTree 275 * {@link WindowTree} from which the target is extracted 276 * @throws InvalidParameterException 277 * thrown if HWND is not contained in windowTree 278 */ 279 public void setTarget(WindowTree windowTree) 280 throws InvalidParameterException { 281 int hwnd = Integer.parseInt(getParameter("window.hwnd")); 282 WindowTreeNode node = windowTree.find(hwnd); 283 if (node == null) { 284 throw new InvalidParameterException("No window with HWND " + hwnd 285 + " found in window tree!"); 286 } else { 287 windowClass = node.getClassName(); 288 resourceId = node.getResourceId(); 289 xmlWindowDescription = node.xmlRepresentation(); 290 parentNames = node.getParentNames(); 291 WindowTreeNode parent = node.getParent(); 292 if (parent == null) { 293 parentClass = ""; 294 } else { 295 parentClass = parent.getClassName(); 296 } 297 } 298 } 299 300 /** 301 * <p> 302 * Sets the LPARAM of a message. 303 * </p> 304 * 305 * @param paramValue 306 * value of the LPARAM 307 */ 308 public void setLPARAM(long paramValue) { 309 LPARAM = paramValue; 310 } 311 312 /** 313 * <p> 314 * Sets the WPARAM of a message. 315 * </p> 316 * 317 * @param paramValue 318 * value of the WPARAM 319 */ 320 public void setWPARAM(long paramValue) { 321 WPARAM = paramValue; 322 } 323 324 /** 325 * <p> 326 * Returns the LPARAM of a message. 327 * </p> 328 * 329 * @return LPARAM of the message 330 */ 331 public long getLPARAM() { 332 return LPARAM; 333 } 334 335 /** 336 * <p> 337 * Returns the WPARAM of a message. 338 * </p> 339 * 340 * @return WPARAM of the message 341 */ 342 public long getWPARAM() { 343 return WPARAM; 344 } 345 346 /** 347 * <p> 348 * If the LPARAM contains a HWND, this function can be used to set a target 349 * string to identify the HWND at run-time. 350 * </p> 351 * 352 * @param windowDesc 353 * target string 354 */ 355 public void setLPARAMasWindowDesc(String windowDesc) { 356 LPARAMasWindowDesc = windowDesc; 357 } 358 359 /** 360 * <p> 361 * If the WPARAM contains a HWND, this function can be used to set a target 362 * string to identify the HWND at run-time. 363 * </p> 364 * 365 * @param windowDesc 366 * target string 367 */ 368 public void setWPARAMasWindowDesc(String windowDesc) { 369 WPARAMasWindowDesc = windowDesc; 370 } 371 372 /** 373 * <p> 374 * If the LPARAM contains a HWND and the target string for the HWND is set, 375 * this function returns the target string. Otherwise, {@code null} is 376 * returned. 377 * </p> 378 * 379 * @return target string if available; {@code null} otherwise 380 */ 381 public String getLPARAMasWindowDesc() { 382 return LPARAMasWindowDesc; 383 } 384 385 /** 386 * <p> 387 * If the WPARAM contains a HWND and the target string for the HWND is set, 388 * this function returns the target string. Otherwise, {@code null} is 389 * returned. 390 * </p> 391 * 392 * @return target string if available; {@code null} otherwise 393 */ 394 public String getWPARAMasWindowDesc() { 395 return WPARAMasWindowDesc; 396 } 397 398 /** 399 * <p> 400 * Returns the target string of the message. 401 * </p> 402 * 403 * @return target string of the message 404 */ 405 public String getXmlWindowDescription() { 406 return xmlWindowDescription; 407 } 408 409 /** 410 * <p> 411 * Sets the target string manually. 412 * </p> 413 * 414 * @param xmlWindowDescription 415 * target string 416 */ 417 public void setXmlWindowDescription(String xmlWindowDescription) { 418 this.xmlWindowDescription = xmlWindowDescription; 419 } 420 421 /** 422 * <p> 423 * Returns the delay after this message during replays. 424 * </p> 425 * 426 * @return delay after this message 427 */ 428 public int getDelay() { 429 return delay; 430 } 431 432 /** 433 * <p> 434 * Sets the delay after this message during replays. 435 * </p> 436 * 437 * @param delay 438 * delay after this message 439 */ 440 public void setDelay(int delay) { 441 this.delay = delay; 442 } 443 444 /** 445 * <p> 446 * Returns the parent names separated by dots, e.g., "GrandParent.Parent". 447 * </p> 448 * 449 * @return names of the parents 450 */ 451 public String getParentNames() { 452 return parentNames; 453 } 454 455 /** 456 * <p> 457 * Returns the window class of the parent. 458 * </p> 459 * 460 * @return window classes of the parents 461 */ 462 public String getParentClass() { 463 return parentClass; 464 } 465 466 /** 467 * <p> 468 * Returns the number of parameters stored together with this message. 469 * </p> 470 * 471 * @return number of parameters stored with this message 472 */ 473 public int getNumParams() { 474 return params.size(); 475 } 476 477 /* 478 * (non-Javadoc) 479 * 480 * @see de.ugoe.cs.quest.eventcore.IReplayable#getReplay() 481 */ 482 @Override 483 public String getReplay() { 484 StringBuilder currentMsgStr = new StringBuilder(400); 485 currentMsgStr.append(" <msg type=\"" + type + "\" "); 486 currentMsgStr.append("LPARAM=\"" + LPARAM + "\" "); 487 currentMsgStr.append("WPARAM=\"" + WPARAM + "\" "); 488 currentMsgStr.append("delay=\"" + delay + "\">"); 489 if (LPARAMasWindowDesc != null) { 490 currentMsgStr.append(StringTools.ENDLINE); 491 currentMsgStr.append(" <LPARAM>"); 492 currentMsgStr.append(StringTools.ENDLINE); 493 currentMsgStr.append(LPARAMasWindowDesc); 494 currentMsgStr.append(StringTools.ENDLINE); 495 currentMsgStr.append("</LPARAM>"); 496 } 497 if (WPARAMasWindowDesc != null) { 498 currentMsgStr.append(StringTools.ENDLINE); 499 currentMsgStr.append(" <WPARAM>"); 500 currentMsgStr.append(StringTools.ENDLINE); 501 currentMsgStr.append(WPARAMasWindowDesc); 502 currentMsgStr.append(StringTools.ENDLINE); 503 currentMsgStr.append(" </WPARAM>"); 504 } 505 currentMsgStr.append(StringTools.ENDLINE); 506 currentMsgStr.append(xmlWindowDescription); 507 currentMsgStr.append(StringTools.ENDLINE); 508 currentMsgStr.append(" </msg>"); 509 currentMsgStr.append(StringTools.ENDLINE); 510 return currentMsgStr.toString(); 511 } 512 513 /* (non-Javadoc) 514 * @see de.ugoe.cs.quest.eventcore.IReplayable#getDecorator() 45 /** 46 * <p> 47 * Two {@link WindowsMessage} are equal, if their {@link #type}, {@link #xmlWindowDescription}, 48 * and {@link #params} are equal. 49 * </p> 50 * 51 * @see java.lang.Object#equals(java.lang.Object) 515 52 */ 516 53 @Override 517 public IReplayDecorator getDecorator() { 518 return MFCReplayDecorator.getInstance(); 54 public boolean equals(Object other) { 55 if (other == this) { 56 return true; 57 } 58 boolean isEqual = false; 59 if (other instanceof MFCEvent) { 60 isEqual = 61 ((MFCEvent) other).type == this.type && 62 ((MFCEvent) other).target.equals(this.target); 63 } 64 return isEqual; 519 65 } 66 67 /* 68 * (non-Javadoc) 69 * 70 * @see java.lang.Object#hashCode() 71 */ 72 @Override 73 public int hashCode() { 74 int multiplier = 17; 75 int hash = 42; 76 77 hash = multiplier * hash + type.hashCode(); 78 hash = multiplier * hash + target.hashCode(); 79 80 return hash; 81 } 82 520 83 } -
trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/eventcore/MFCEventType.java
r566 r619 1 1 // Module : $RCSfile: MFCEventType.java,v $ 2 // Version : $Revision: 0.0 $ $Author: pharms $ $Date: 21.08.2012 $ 3 // Project : quest-plugin-mfc 4 // Creation : 2012 by pharms 5 // Copyright : Patrick Harms, 2012 2 6 package de.ugoe.cs.quest.plugin.mfc.eventcore; 3 7 … … 9 13 * </p> 10 14 * 11 * @version $Revision: $ $Date: Aug 17,2012$12 * @author 2012, last modified by $Author: sherbold$15 * @version $Revision: $ $Date: 21.08.2012$ 16 * @author 2012, last modified by $Author: pharms$ 13 17 */ 14 18 public class MFCEventType implements IEventType { 15 19 16 /** 20 /** */ 17 21 private static final long serialVersionUID = 1L; 22 23 /** */ 24 private WindowsMessageType messageType; 18 25 19 private final String typeString; 20 21 private String info = null; 22 23 public MFCEventType(String typeString) { 24 this.typeString = typeString; 25 } 26 27 public void setInfo(String info) { 28 this.info = info; 26 /** 27 * <p> 28 * TODO: comment 29 * </p> 30 * 31 * @param currentMessageType 32 */ 33 public MFCEventType(WindowsMessageType messageType) { 34 this.messageType = messageType; 29 35 } 30 36 31 /* 32 * (non-Javadoc) 33 * 37 /* (non-Javadoc) 34 38 * @see de.ugoe.cs.quest.eventcore.IEventType#getName() 35 39 */ 36 40 @Override 37 41 public String getName() { 38 return "MFCEventType"; 39 } 40 41 /* 42 * (non-Javadoc) 43 * 44 * @see java.lang.Object#toString() 45 */ 46 @Override 47 public String toString() { 48 String str = typeString; 49 if( info!=null ) { 50 str += "." + info; 51 } 52 return str; 42 return messageType.name(); 53 43 } 54 44 -
trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/eventcore/WindowsMessage.java
r566 r619 1 1 2 package de.ugoe.cs.quest.plugin.mfc.eventcore; 2 3 3 import java.security.InvalidParameterException;4 4 import java.util.HashMap; 5 5 import java.util.Map; 6 6 7 import de.ugoe.cs.quest.IReplayDecorator; 8 import de.ugoe.cs.quest.eventcore.IReplayable; 9 import de.ugoe.cs.quest.plugin.mfc.MFCReplayDecorator; 10 import de.ugoe.cs.util.StringTools; 7 import de.ugoe.cs.quest.plugin.mfc.guimodel.MFCGUIElement; 11 8 12 9 /** 13 10 * <p> 14 * Contains all informations about a windows message, i.e., all parameters that 15 * are read when awindows message is parsed as well as its target, hwnd, etc.11 * Contains all informations about a windows message, i.e., all parameters that are read when a 12 * windows message is parsed as well as its target, hwnd, etc. 16 13 * </p> 17 14 * … … 20 17 * 21 18 */ 22 public class WindowsMessage implements IReplayable { 23 24 /** 25 * <p> 26 * Id for object serialization. 27 * </p> 28 */ 29 private static final long serialVersionUID = 1L; 30 31 /** 32 * <p> 33 * Type of the message. 34 * </p> 35 */ 36 final int type; 37 38 /** 39 * <p> 40 * Window class of the message target. Default: "" 41 * </p> 42 */ 43 private String windowClass = ""; 44 45 /** 46 * <p> 47 * Resource Id of the message target. Default: 0 48 * </p> 49 */ 50 private int resourceId = 0; 51 52 /** 53 * <p> 54 * XML representation of the message target. 55 * </p> 56 */ 57 private String xmlWindowDescription = ""; 58 59 /** 60 * <p> 61 * String that contains the names of all parent widgets and itself, separated by dots, 62 * e.g., "GrandParent.Parent.self". 63 * </p> 64 */ 65 private String parentNames = null; 66 67 /** 68 * <p> 69 * String that contains the window class of the parent widget. 70 * </p> 71 */ 72 private String parentClass = null; 73 74 /** 75 * <p> 76 * LPARAM of the message. Default: 0 77 * </p> 78 */ 79 private long LPARAM = 0; 80 81 /** 82 * <p> 83 * WPARAM of the message. Default: 0 84 * </p> 85 */ 86 private long WPARAM = 0; 87 88 /** 89 * <p> 90 * If the LPARAM contains a HWND, this string stores the target of the HWND. 91 * </p> 92 */ 93 private String LPARAMasWindowDesc = null; 94 95 /** 96 * <p> 97 * If the WPARAM contains a HWND, this string stores the target of the HWND. 98 * </p> 99 */ 100 private String WPARAMasWindowDesc = null; 101 102 /** 103 * <p> 104 * Delay after sending the messages during a replay. Default: 0 105 * </p> 106 */ 107 private int delay = 0; 108 109 /** 110 * <p> 111 * A map of all parameters, associated with the message, created during the 112 * parsing of messages from the logs {@code param}-nodes. 113 * </p> 114 */ 115 private Map<String, String> params = new HashMap<String, String>(); 116 117 /** 118 * <p> 119 * Constructor. Creates a new message with a given message type. 120 * </p> 121 * 122 * @param type 123 * type of the message 124 */ 125 public WindowsMessage(int type) { 126 this.type = type; 127 } 128 129 /** 130 * <p> 131 * Adds a parameter to the message. 132 * </p> 133 * 134 * @param type 135 * type descriptor of the parameter 136 * @param value 137 * value of the parameter 138 */ 139 public void addParameter(String type, String value) { 140 params.put(type, value); 141 if (type.equals("LPARAM")) { 142 LPARAM = Long.parseLong(value); 143 } else if (type.equals("WPARAM")) { 144 WPARAM = Long.parseLong(value); 145 } 146 } 147 148 /** 149 * <p> 150 * Returns the type of the message. 151 * </p> 152 * 153 * @return type of the message 154 */ 155 public int getType() { 156 return type; 157 } 158 159 /** 160 * <p> 161 * Returns the value of a parameter, given its type. If the parameter is not 162 * found, {@code null} is returned. 163 * </p> 164 * 165 * @param type 166 * type of the parameter 167 * @return value of the parameter 168 */ 169 public String getParameter(String type) { 170 return params.get(type); 171 } 172 173 /** 174 * <p> 175 * Returns the window class of the message target. 176 * </p> 177 * 178 * @return window class of the message target 179 */ 180 public String getWindowClass() { 181 return windowClass; 182 } 183 184 /** 185 * <p> 186 * Returns the HWND the message is addressed to. 187 * </p> 188 * 189 * @return HWND the message is addressed to 190 */ 191 public int getHwnd() { 192 int hwnd = -1; 193 String hwndString = getParameter("window.hwnd"); // possible, as 194 // "window.hwnd" is 195 // mandatory 196 if (hwndString != null) { 197 hwnd = Integer.parseInt(hwndString); 198 } 199 return hwnd; 200 } 201 202 /** 203 * <p> 204 * Returns the resource Id of the message target. 205 * </p> 206 * 207 * @return resource Id of the message target 208 */ 209 public int getWindowResourceId() { 210 return resourceId; 211 } 212 213 /** 214 * <p> 215 * Two {@link WindowsMessage} are equal, if their {@link #type}, 216 * {@link #xmlWindowDescription}, and {@link #params} are equal. 217 * </p> 218 * 219 * @see java.lang.Object#equals(java.lang.Object) 220 */ 221 @Override 222 public boolean equals(Object other) { 223 if (other == this) { 224 return true; 225 } 226 boolean isEqual = false; 227 if (other instanceof WindowsMessage) { 228 isEqual = ((WindowsMessage) other).type == this.type 229 && ((WindowsMessage) other).xmlWindowDescription 230 .equals(this.xmlWindowDescription) 231 && ((WindowsMessage) other).params.equals(this.params); 232 } 233 return isEqual; 234 } 235 236 /* 237 * (non-Javadoc) 238 * 239 * @see java.lang.Object#hashCode() 240 */ 241 @Override 242 public int hashCode() { 243 int multiplier = 17; 244 int hash = 42; 245 246 hash = multiplier * hash + type; 247 hash = multiplier * hash + xmlWindowDescription.hashCode(); 248 hash = multiplier * hash + params.hashCode(); 249 250 return hash; 251 } 252 253 /** 254 * <p> 255 * Returns a string representation of the message of the form 256 * "msg[target=HWND;type=TYPE]". 257 * </p> 258 * 259 * @see java.lang.Object#toString() 260 */ 261 @Override 262 public String toString() { 263 return "msg[target=" + getParameter("window.hwnd") + ";type=" + type 264 + "]"; 265 } 266 267 /** 268 * <p> 269 * Retrieves the target string of a message from a given {@link WindowTree} 270 * through looking up the HWND the message is addressed to in the window 271 * tree. 272 * </p> 273 * 274 * @param windowTree 275 * {@link WindowTree} from which the target is extracted 276 * @throws InvalidParameterException 277 * thrown if HWND is not contained in windowTree 278 */ 279 public void setTarget(WindowTree windowTree) 280 throws InvalidParameterException { 281 int hwnd = Integer.parseInt(getParameter("window.hwnd")); 282 WindowTreeNode node = windowTree.find(hwnd); 283 if (node == null) { 284 throw new InvalidParameterException("No window with HWND " + hwnd 285 + " found in window tree!"); 286 } else { 287 windowClass = node.getClassName(); 288 resourceId = node.getResourceId(); 289 xmlWindowDescription = node.xmlRepresentation(); 290 parentNames = node.getParentNames(); 291 WindowTreeNode parent = node.getParent(); 292 if (parent == null) { 293 parentClass = ""; 294 } else { 295 parentClass = parent.getClassName(); 296 } 297 } 298 } 299 300 /** 301 * <p> 302 * Sets the LPARAM of a message. 303 * </p> 304 * 305 * @param paramValue 306 * value of the LPARAM 307 */ 308 public void setLPARAM(long paramValue) { 309 LPARAM = paramValue; 310 } 311 312 /** 313 * <p> 314 * Sets the WPARAM of a message. 315 * </p> 316 * 317 * @param paramValue 318 * value of the WPARAM 319 */ 320 public void setWPARAM(long paramValue) { 321 WPARAM = paramValue; 322 } 323 324 /** 325 * <p> 326 * Returns the LPARAM of a message. 327 * </p> 328 * 329 * @return LPARAM of the message 330 */ 331 public long getLPARAM() { 332 return LPARAM; 333 } 334 335 /** 336 * <p> 337 * Returns the WPARAM of a message. 338 * </p> 339 * 340 * @return WPARAM of the message 341 */ 342 public long getWPARAM() { 343 return WPARAM; 344 } 345 346 /** 347 * <p> 348 * If the LPARAM contains a HWND, this function can be used to set a target 349 * string to identify the HWND at run-time. 350 * </p> 351 * 352 * @param windowDesc 353 * target string 354 */ 355 public void setLPARAMasWindowDesc(String windowDesc) { 356 LPARAMasWindowDesc = windowDesc; 357 } 358 359 /** 360 * <p> 361 * If the WPARAM contains a HWND, this function can be used to set a target 362 * string to identify the HWND at run-time. 363 * </p> 364 * 365 * @param windowDesc 366 * target string 367 */ 368 public void setWPARAMasWindowDesc(String windowDesc) { 369 WPARAMasWindowDesc = windowDesc; 370 } 371 372 /** 373 * <p> 374 * If the LPARAM contains a HWND and the target string for the HWND is set, 375 * this function returns the target string. Otherwise, {@code null} is 376 * returned. 377 * </p> 378 * 379 * @return target string if available; {@code null} otherwise 380 */ 381 public String getLPARAMasWindowDesc() { 382 return LPARAMasWindowDesc; 383 } 384 385 /** 386 * <p> 387 * If the WPARAM contains a HWND and the target string for the HWND is set, 388 * this function returns the target string. Otherwise, {@code null} is 389 * returned. 390 * </p> 391 * 392 * @return target string if available; {@code null} otherwise 393 */ 394 public String getWPARAMasWindowDesc() { 395 return WPARAMasWindowDesc; 396 } 397 398 /** 399 * <p> 400 * Returns the target string of the message. 401 * </p> 402 * 403 * @return target string of the message 404 */ 405 public String getXmlWindowDescription() { 406 return xmlWindowDescription; 407 } 408 409 /** 410 * <p> 411 * Sets the target string manually. 412 * </p> 413 * 414 * @param xmlWindowDescription 415 * target string 416 */ 417 public void setXmlWindowDescription(String xmlWindowDescription) { 418 this.xmlWindowDescription = xmlWindowDescription; 419 } 420 421 /** 422 * <p> 423 * Returns the delay after this message during replays. 424 * </p> 425 * 426 * @return delay after this message 427 */ 428 public int getDelay() { 429 return delay; 430 } 431 432 /** 433 * <p> 434 * Sets the delay after this message during replays. 435 * </p> 436 * 437 * @param delay 438 * delay after this message 439 */ 440 public void setDelay(int delay) { 441 this.delay = delay; 442 } 443 444 /** 445 * <p> 446 * Returns the parent names separated by dots, e.g., "GrandParent.Parent". 447 * </p> 448 * 449 * @return names of the parents 450 */ 451 public String getParentNames() { 452 return parentNames; 453 } 454 455 /** 456 * <p> 457 * Returns the window class of the parent. 458 * </p> 459 * 460 * @return window classes of the parents 461 */ 462 public String getParentClass() { 463 return parentClass; 464 } 465 466 /** 467 * <p> 468 * Returns the number of parameters stored together with this message. 469 * </p> 470 * 471 * @return number of parameters stored with this message 472 */ 473 public int getNumParams() { 474 return params.size(); 475 } 476 477 /* 478 * (non-Javadoc) 479 * 480 * @see de.ugoe.cs.quest.eventcore.IReplayable#getReplay() 481 */ 482 @Override 483 public String getReplay() { 484 StringBuilder currentMsgStr = new StringBuilder(400); 485 currentMsgStr.append(" <msg type=\"" + type + "\" "); 486 currentMsgStr.append("LPARAM=\"" + LPARAM + "\" "); 487 currentMsgStr.append("WPARAM=\"" + WPARAM + "\" "); 488 currentMsgStr.append("delay=\"" + delay + "\">"); 489 if (LPARAMasWindowDesc != null) { 490 currentMsgStr.append(StringTools.ENDLINE); 491 currentMsgStr.append(" <LPARAM>"); 492 currentMsgStr.append(StringTools.ENDLINE); 493 currentMsgStr.append(LPARAMasWindowDesc); 494 currentMsgStr.append(StringTools.ENDLINE); 495 currentMsgStr.append("</LPARAM>"); 496 } 497 if (WPARAMasWindowDesc != null) { 498 currentMsgStr.append(StringTools.ENDLINE); 499 currentMsgStr.append(" <WPARAM>"); 500 currentMsgStr.append(StringTools.ENDLINE); 501 currentMsgStr.append(WPARAMasWindowDesc); 502 currentMsgStr.append(StringTools.ENDLINE); 503 currentMsgStr.append(" </WPARAM>"); 504 } 505 currentMsgStr.append(StringTools.ENDLINE); 506 currentMsgStr.append(xmlWindowDescription); 507 currentMsgStr.append(StringTools.ENDLINE); 508 currentMsgStr.append(" </msg>"); 509 currentMsgStr.append(StringTools.ENDLINE); 510 return currentMsgStr.toString(); 511 } 512 513 /* (non-Javadoc) 514 * @see de.ugoe.cs.quest.eventcore.IReplayable#getDecorator() 19 public class WindowsMessage { 20 21 /** 22 * <p> 23 * Type of the message. 24 * </p> 25 */ 26 final WindowsMessageType type; 27 28 /** 29 * <p> 30 * LPARAM of the message. Default: 0 31 * </p> 32 */ 33 private long LPARAM = 0; 34 35 /** 36 * <p> 37 * WPARAM of the message. Default: 0 38 * </p> 39 */ 40 private long WPARAM = 0; 41 42 /** 43 * <p> 44 * A map of all parameters, associated with the message, created during the parsing of messages 45 * from the logs {@code param}-nodes. 46 * </p> 47 */ 48 private Map<String, Object> params = new HashMap<String, Object>(); 49 50 /** 51 * <p> 52 * the target GUI element to which the message was sent 53 * </p> 54 */ 55 private MFCGUIElement target; 56 57 /** 58 * <p> 59 * an XML representation of the target to preserve it as it was when this message was created 60 * </p> 61 */ 62 protected String targetXML; 63 64 /** 65 * <p> 66 * Constructor. Creates a new message with a given message type. 67 * </p> 68 * 69 * @param type 70 * type of the message 71 * @param currentMessageParameters 72 * @param target 73 */ 74 public WindowsMessage(WindowsMessageType type, 75 MFCGUIElement target, 76 Map<String, Object> messageParameters) 77 { 78 this.type = type; 79 setTarget(target); 80 81 for (Map.Entry<String, Object> entry : messageParameters.entrySet()) { 82 addParameter(entry.getKey(), entry.getValue()); 83 } 84 } 85 86 /** 87 * <p> 88 * Constructor. Creates a new message with a given message type. 89 * </p> 90 * 91 * @param type 92 * type of the message 93 */ 94 public WindowsMessage(WindowsMessageType type) 95 { 96 this.type = type; 97 } 98 99 /** 100 * <p> 101 * Adds a parameter to the message. 102 * </p> 103 * 104 * @param type 105 * type descriptor of the parameter 106 * @param value 107 * value of the parameter 108 */ 109 public void addParameter(String type, Object value) { 110 params.put(type, value); 111 if (type.equals("LPARAM")) { 112 LPARAM = (Long) value; 113 } 114 else if (type.equals("WPARAM")) { 115 WPARAM = (Long) value; 116 } 117 } 118 119 /** 120 * <p> 121 * Returns the type of the message. 122 * </p> 123 * 124 * @return type of the message 125 */ 126 public WindowsMessageType getType() { 127 return type; 128 } 129 130 /** 131 * <p> 132 * TODO: comment 133 * </p> 134 * 135 * @param target2 136 */ 137 public void setTarget(MFCGUIElement target) { 138 this.target = target; 139 this.targetXML = target.toXML(); 140 } 141 142 /** 143 * <p> 144 * TODO: comment 145 * </p> 146 * 147 * @return 148 */ 149 public MFCGUIElement getTarget() { 150 return target; 151 } 152 153 /** 154 * <p> 155 * Returns the value of a parameter, given its type. If the parameter is not found, {@code null} 156 * is returned. 157 * </p> 158 * 159 * @param type 160 * type of the parameter 161 * @return value of the parameter 162 */ 163 public Object getParameter(String type) { 164 return params.get(type); 165 } 166 167 /** 168 * <p> 169 * Two {@link WindowsMessage} are equal, if their {@link #type}, {@link #xmlWindowDescription}, 170 * and {@link #params} are equal. 171 * </p> 172 * 173 * @see java.lang.Object#equals(java.lang.Object) 515 174 */ 516 175 @Override 517 public IReplayDecorator getDecorator() { 518 return MFCReplayDecorator.getInstance(); 519 } 176 public boolean equals(Object other) { 177 if (other == this) { 178 return true; 179 } 180 boolean isEqual = false; 181 if (other instanceof WindowsMessage) { 182 isEqual = 183 ((WindowsMessage) other).type == this.type && 184 ((WindowsMessage) other).target.equals(this.target) && 185 ((WindowsMessage) other).params.equals(this.params); 186 } 187 return isEqual; 188 } 189 190 /* 191 * (non-Javadoc) 192 * 193 * @see java.lang.Object#hashCode() 194 */ 195 @Override 196 public int hashCode() { 197 int multiplier = 17; 198 int hash = 42; 199 200 hash = multiplier * hash + type.hashCode(); 201 hash = multiplier * hash + target.hashCode(); 202 hash = multiplier * hash + params.hashCode(); 203 204 return hash; 205 } 206 207 /** 208 * <p> 209 * Returns a string representation of the message of the form "msg[target=HWND;type=TYPE]". 210 * </p> 211 * 212 * @see java.lang.Object#toString() 213 */ 214 @Override 215 public String toString() { 216 return "msg[target=" + getParameter("window.hwnd") + ";type=" + type + "]"; 217 } 218 219 /** 220 * <p> 221 * Returns the LPARAM of a message. 222 * </p> 223 * 224 * @return LPARAM of the message 225 */ 226 public long getLPARAM() { 227 return LPARAM; 228 } 229 230 /** 231 * <p> 232 * Returns the WPARAM of a message. 233 * </p> 234 * 235 * @return WPARAM of the message 236 */ 237 public long getWPARAM() { 238 return WPARAM; 239 } 240 241 /** 242 * <p> 243 * Returns the number of parameters stored together with this message. 244 * </p> 245 * 246 * @return number of parameters stored with this message 247 */ 248 public int getNumParams() { 249 return params.size(); 250 } 251 252 /** 253 * <p> 254 * TODO: comment 255 * </p> 256 * 257 * @return 258 */ 259 protected Map<String, Object> getParameters() { 260 return params; 261 } 262 263 /** 264 * <p> 265 * TODO: comment 266 * </p> 267 * 268 * @return 269 */ 270 public String getTargetXML() { 271 return targetXML; 272 } 273 520 274 } -
trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/eventcore/WindowsMessageType.java
r578 r619 1 // Module : $RCSfile: MessageType.java,v $ 2 // Version : $Revision: 0.0 $ $Author: Patrick $ $Date: 26.11.2011 14:36:45 $ 3 // Project : TaskTreePerformanceTest 4 // Creation : 2011 by Patrick 5 // Copyright : Patrick Harms, 2011 1 6 2 7 package de.ugoe.cs.quest.plugin.mfc.eventcore; 3 8 4 import de.ugoe.cs.quest.eventcore.IEventType;5 6 9 /** 7 * <p>8 10 * TODO comment 9 * </p>10 11 * 11 * @version $Revision: $ $Date: Aug 17, 2012$12 * @author 201 2, last modified by $Author: sherbold$12 * @version $Revision: $ $Date: $ 13 * @author 2011, last modified by $Author: $ 13 14 */ 14 public class MFCEventType implements IEventType { 15 16 /** */ 17 private static final long serialVersionUID = 1L; 18 19 private final String typeString; 15 public enum WindowsMessageType { 20 16 21 private String info = null; 22 23 public MFCEventType(String typeString) { 24 this.typeString = typeString; 25 } 26 27 public void setInfo(String info) { 28 this.info = info; 29 } 30 31 /* 32 * (non-Javadoc) 17 WM_NULL(0), 18 WM_CREATE(1), 19 WM_DESTROY(2), 20 WM_MOVE(3), 21 WM_SIZE(5), 22 WM_ACTIVATE(6), 23 WM_SETFOCUS(7), 24 WM_KILLFOCUS(8), 25 WM_ENABLE(10), 26 WM_SETREDRAW(11), 27 WM_SETTEXT(12), 28 WM_GETTEXT(13), 29 WM_GETTEXTLENGTH(14), 30 WM_PAINT(15), 31 WM_CLOSE(16), 32 WM_QUERYENDSESSION(17), 33 WM_QUIT(18), 34 WM_QUERYOPEN(19), 35 WM_ERASEBKGND(20), 36 WM_SYSCOLORCHANGE(21), 37 WM_ENDSESSION(22), 38 WM_SHOWWINDOW(24), 39 WM_CTLCOLOR(25), 40 WM_WININICHANGE(26), 41 WM_DEVMODECHANGE(27), 42 WM_ACTIVATEAPP(28), 43 WM_FONTCHANGE(29), 44 WM_TIMECHANGE(30), 45 WM_CANCELMODE(31), 46 WM_SETCURSOR(32), 47 WM_MOUSEACTIVATE(33), 48 WM_CHILDACTIVATE(34), 49 WM_QUEUESYNC(35), 50 WM_GETMINMAXINFO(36), 51 WM_PAINTICON(38), 52 WM_ICONERASEBKGND(39), 53 WM_NEXTDLGCTL(40), 54 WM_SPOOLERSTATUS(42), 55 WM_DRAWITEM(43), 56 WM_MEASUREITEM(44), 57 WM_DELETEITEM(45), 58 WM_VKEYTOITEM(46), 59 WM_CHARTOITEM(47), 60 WM_SETFONT(48), 61 WM_GETFONT(49), 62 WM_SETHOTKEY(50), 63 WM_GETHOTKEY(51), 64 WM_QUERYDRAGICON(55), 65 WM_COMPAREITEM(57), 66 WM_GETOBJECT(61), 67 WM_COMPACTING(65), 68 WM_COMMNOTIFY(68), 69 WM_WINDOWPOSCHANGING(70), 70 WM_WINDOWPOSCHANGED(71), 71 WM_POWER(72), 72 WM_COPYDATA(74), 73 WM_CANCELJOURNAL(75), 74 WM_NOTIFY(78), 75 WM_INPUTLANGCHANGEREQUEST(80), 76 WM_INPUTLANGCHANGE(81), 77 WM_TCARD(82), 78 WM_HELP(83), 79 WM_USERCHANGED(84), 80 WM_NOTIFYFORMAT(85), 81 WM_CONTEXTMENU(123), 82 WM_STYLECHANGING(124), 83 WM_STYLECHANGED(125), 84 WM_DISPLAYCHANGE(126), 85 WM_GETICON(127), 86 WM_SETICON(128), 87 WM_NCCREATE(129), 88 WM_NCDESTROY(130), 89 WM_NCCALCSIZE(131), 90 WM_NCHITTEST(132), 91 WM_NCPAINT(133), 92 WM_NCACTIVATE(134), 93 WM_GETDLGCODE(135), 94 WM_SYNCPAINT(136), 95 WM_NCMOUSEMOVE(160), 96 WM_NCLBUTTONDOWN(161), 97 WM_NCLBUTTONUP(162), 98 WM_NCLBUTTONDBLCLK(163), 99 WM_NCRBUTTONDOWN(164), 100 WM_NCRBUTTONUP(165), 101 WM_NCRBUTTONDBLCLK(166), 102 WM_NCMBUTTONDOWN(167), 103 WM_NCMBUTTONUP(168), 104 WM_NCMBUTTONDBLCLK(169), 105 WM_NCXBUTTONDOWN(171), 106 WM_NCXBUTTONUP(172), 107 WM_NCXBUTTONDBLCLK(173), 108 SBM_SETPOS(224), 109 BM_CLICK(245), 110 WM_INPUT(255), 111 WM_KEYDOWN(256), 112 WM_KEYFIRST(256), 113 WM_KEYUP(257), 114 WM_CHAR(258), 115 WM_DEADCHAR(259), 116 WM_SYSKEYDOWN(260), 117 WM_SYSKEYUP(261), 118 WM_SYSCHAR(262), 119 WM_SYSDEADCHAR(263), 120 WM_KEYLAST(264), 121 WM_WNT_CONVERTREQUESTEX(265), 122 WM_CONVERTREQUEST(266), 123 WM_CONVERTRESULT(267), 124 WM_INTERIM(268), 125 WM_IME_STARTCOMPOSITION(269), 126 WM_IME_ENDCOMPOSITION(270), 127 WM_IME_COMPOSITION(271), 128 WM_IME_KEYLAST(271), 129 WM_INITDIALOG(272), 130 WM_COMMAND(273), 131 WM_SYSCOMMAND(274), 132 WM_TIMER(275), 133 WM_HSCROLL(276), 134 WM_VSCROLL(277), 135 WM_INITMENU(278), 136 WM_INITMENUPOPUP(279), 137 WM_MENUSELECT(287), 138 WM_MENUCHAR(288), 139 WM_ENTERIDLE(289), 140 WM_MENURBUTTONUP(290), 141 WM_MENUDRAG(291), 142 WM_MENUGETOBJECT(292), 143 WM_UNINTMENUPOPUP(293), 144 WM_MENUCOMMAND(294), 145 WM_CHANGEUISTATE(295), 146 WM_UPDATEUISTATE(296), 147 WM_QUERYUISTATE(297), 148 WM_CTLCOLORMSGBOX(306), 149 WM_CTLCOLOREDIT(307), 150 WM_CTLCOLORLISTBOX(308), 151 WM_CTLCOLORBTN(309), 152 WM_CTLCOLORDLG(310), 153 WM_CTLCOLORSCROLLBAR(311), 154 WM_CTLCOLORSTATIC(312), 155 CB_SHOWDROPDOWN(335), 156 LB_SETCURSEL(390), 157 WM_MOUSEFIRST(512), 158 WM_MOUSEMOVE(512), 159 WM_LBUTTONDOWN(513), 160 WM_LBUTTONUP(514), 161 WM_LBUTTONDBLCLK(515), 162 WM_RBUTTONDOWN(516), 163 WM_RBUTTONUP(517), 164 WM_RBUTTONDBLCLK(518), 165 WM_MBUTTONDOWN(519), 166 WM_MBUTTONUP(520), 167 WM_MBUTTONDBLCLK(521), 168 WM_MOUSELAST(521), 169 WM_MOUSEWHEEL(522), 170 WM_XBUTTONDOWN(523), 171 WM_XBUTTONUP(524), 172 WM_XBUTTONDBLCLK(525), 173 WM_USER(1024), 174 CB_SETCURSEL(334), 175 TBM_SETPOS(1029), 176 UDM_SETRANGE(1125), 177 TCM_SETCURSEL(4876); 178 179 /** the numerical representation of the message type */ 180 private int mNumber; 181 182 /** 183 * @param number 184 */ 185 WindowsMessageType(int number) { 186 mNumber = number; 187 } 188 189 /** 190 * @return Returns the number. 191 */ 192 public int getNumber() { 193 return mNumber; 194 } 195 196 /** 197 * <p> 198 * Checks if the type of a message generated is a keyboard interaction. 199 * </p> 33 200 * 34 * @see de.ugoe.cs.quest.eventcore.IEventType#getName() 35 */ 36 @Override 37 public String getName() { 38 return "MFCEventType"; 39 } 40 41 /* 42 * (non-Javadoc) 201 * @param msgType 202 * type of the message 203 * @return true if it is a keyboard interaction; false otherwise 204 */ 205 public boolean isKeyMessage() { 206 boolean isKeyMsg = false; 207 switch (this) 208 { 209 case WM_KEYDOWN: 210 case WM_KEYUP: 211 case WM_SYSKEYDOWN: 212 case WM_SYSKEYUP: 213 isKeyMsg = true; 214 break; 215 default: 216 break; 217 } 218 return isKeyMsg; 219 } 220 221 /** 222 * <p> 223 * Checks if the type of a message indicates that the mouse has been pressed down. 224 * </p> 43 225 * 44 * @see java.lang.Object#toString() 45 */ 46 @Override 47 public String toString() { 48 String str = typeString; 49 if( info!=null ) { 50 str += "." + info; 51 } 52 return str; 53 } 54 226 * @param msgType 227 * type of the message 228 * @return true if it is mouse-down message; false otherwise 229 */ 230 public boolean isDownMessage() { 231 boolean isDownMsg = false; 232 switch (this) 233 { 234 case WM_LBUTTONDOWN: 235 case WM_RBUTTONDOWN: 236 case WM_MBUTTONDOWN: 237 case WM_XBUTTONDOWN: 238 case WM_NCLBUTTONDOWN: 239 case WM_NCRBUTTONDOWN: 240 case WM_NCMBUTTONDOWN: 241 case WM_NCXBUTTONDOWN: 242 isDownMsg = true; 243 break; 244 default: 245 break; 246 } 247 return isDownMsg; 248 } 249 250 /** 251 * <p> 252 * Checks if the type of a message indicates that a double click has been performed. 253 * </p> 254 * 255 * @param msgType 256 * type of the message 257 * @return true if it is a double click message; false otherwise 258 */ 259 public boolean isDblclkMessage() { 260 boolean isDblclkMsg = false; 261 switch (this) 262 { 263 case WM_LBUTTONDBLCLK: 264 case WM_RBUTTONDBLCLK: 265 case WM_MBUTTONDBLCLK: 266 case WM_XBUTTONDBLCLK: 267 case WM_NCLBUTTONDBLCLK: 268 case WM_NCRBUTTONDBLCLK: 269 case WM_NCMBUTTONDBLCLK: 270 case WM_NCXBUTTONDBLCLK: 271 isDblclkMsg = true; 272 break; 273 default: 274 break; 275 } 276 return isDblclkMsg; 277 } 278 279 /** 280 * <p> 281 * Checks if the type of a message indicates that the mouse has been released. 282 * </p> 283 * 284 * @param msgType 285 * type of the message 286 * @return true if it is mouse-up message; false otherwise 287 */ 288 public boolean isUpMessage() { 289 boolean isUpMsg = false; 290 switch (this) 291 { 292 case WM_LBUTTONUP: 293 case WM_RBUTTONUP: 294 case WM_MBUTTONUP: 295 case WM_XBUTTONUP: 296 case WM_NCLBUTTONUP: 297 case WM_NCRBUTTONUP: 298 case WM_NCMBUTTONUP: 299 case WM_NCXBUTTONUP: 300 isUpMsg = true; 301 break; 302 default: 303 break; 304 } 305 return isUpMsg; 306 } 307 308 /** 309 * 310 */ 311 public static WindowsMessageType parseMessageType(String numberString) { 312 try { 313 int number = Integer.parseInt(numberString); 314 return valueOf(number); 315 } 316 catch (NumberFormatException e) { 317 return WindowsMessageType.valueOf(WindowsMessageType.class, numberString); 318 } 319 } 320 321 /** 322 * 323 */ 324 public static WindowsMessageType valueOf(int number) { 325 for (WindowsMessageType type : WindowsMessageType.values()) { 326 if (type.mNumber == number) { 327 return type; 328 } 329 } 330 331 throw new IllegalArgumentException("there is no message type with number " + number); 332 } 55 333 } -
trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/guimodel/MFCGUIElementSpec.java
r578 r619 1 package de.ugoe.cs.quest.plugin.mfc.eventcore; 1 2 package de.ugoe.cs.quest.plugin.mfc.guimodel; 2 3 3 4 import java.util.ArrayList; 4 5 import java.util.List; 5 6 7 import de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec; 6 8 import de.ugoe.cs.util.StringTools; 7 9 8 10 /** 9 11 * <p> 10 * This class implements a node in the {@link WindowTree} that is maintained 11 * during parsing asession.12 * This class implements a node in the {@link WindowTree} that is maintained during parsing a 13 * session. 12 14 * </p> 13 15 * <p> 14 * The window tree is structure that contains the hierarchy of the windows of a 15 * application as well as basic information about each window: the hwnd; its 16 * name; its resource id; its class name. 16 * The window tree is structure that contains the hierarchy of the windows of a application as well 17 * as basic information about each window: the hwnd; its name; its resource id; its class name. 17 18 * </p> 18 19 * … … 20 21 * @version 1.0 21 22 */ 22 public class WindowTreeNode { 23 24 /** 25 * <p> 26 * Name of the window. May change over time. 27 * </p> 28 */ 29 private String windowName; 30 31 /** 32 * <p> 33 * Handle of the window. Used as unique identifier during its existence. 34 * </p> 35 */ 36 private final int hwnd; 37 38 /** 39 * <p> 40 * Resource id of the window. 41 * </p> 42 */ 43 private final int resourceId; 44 45 /** 46 * <p> 47 * Class name of the window. 48 * </p> 49 */ 50 private final String className; 51 52 /** 53 * <p> 54 * True, if the window is modal. 55 * </p> 56 */ 57 private final boolean isModal; 58 59 /** 60 * <p> 61 * Parent of the window. <code>null</code> if the window has no parent. 62 * </p> 63 */ 64 private WindowTreeNode parent; 65 66 /** 67 * <p> 68 * List of the windows children. May be empty. 69 * </p> 70 */ 71 private List<WindowTreeNode> children; 72 73 /** 74 * <p> 75 * Creates a new WindowTreeNode. 76 * </p> 77 * <p> 78 * The constructor is protected WindowTreeNode may only be created from the 79 * WindowTree. 80 * </p> 81 * 82 * @param hwnd 83 * hwnd of the window 84 * @param parent 85 * reference to the parent's WindowTreeNode 86 * @param windowName 87 * name of the window 88 * @param resourceId 89 * resource id of the window 90 * @param className 91 * class name of the window 92 * @param isModal 93 * modality of the window 94 */ 95 protected WindowTreeNode(int hwnd, WindowTreeNode parent, 96 String windowName, int resourceId, String className, boolean isModal) { 97 this.hwnd = hwnd; 98 this.parent = parent; 99 this.windowName = windowName; 100 this.resourceId = resourceId; 101 this.className = className; 102 this.isModal = isModal; 103 children = new ArrayList<WindowTreeNode>(); 104 } 105 106 /** 107 * <p> 108 * Returns a reference to the WindowTreeNode of the parent. 109 * </p> 110 * 111 * @return WindowTreeNode of the parent 112 */ 113 public WindowTreeNode getParent() { 114 return parent; 115 } 116 117 /** 118 * <p> 119 * Returns the list of the windows children. 120 * </p> 121 * 122 * @return list of the windows children 123 */ 124 public List<WindowTreeNode> getChildren() { 125 return children; 126 } 127 128 /** 129 * <p> 130 * Returns the name of the window. 131 * </p> 132 * 133 * @return name of the window 134 */ 135 public String getName() { 136 return windowName; 137 } 138 139 /** 140 * <p> 141 * Returns the hwnd of the window. 142 * </p> 143 * 144 * @return hwnd of the window 145 */ 146 public int getHwnd() { 147 return hwnd; 148 } 149 150 /** 151 * <p> 152 * Returns the resource id of the window. 153 * </p> 154 * 155 * @return resource id of the window 156 */ 157 public int getResourceId() { 158 return resourceId; 159 } 160 161 /** 162 * <p> 163 * Returns the class name of the window. 164 * </p> 165 * 166 * @return class name of the window 167 */ 168 public String getClassName() { 169 return className; 170 } 171 172 /** 173 * <p> 174 * Sets the name of the window. 175 * </p> 176 * 177 * @param text 178 * new name of the window 179 */ 180 public void setName(String text) { 181 windowName = text; 182 } 183 184 /** 185 * <p> 186 * Removes a the window and all its children from the {@link WindowTree}. 187 * </p> 188 * 189 * @return list of the children of the window for further clean up. 190 */ 191 public List<WindowTreeNode> remove() { 192 if (parent != null) { 193 parent.removeChild(this); 194 } 195 return children; 196 } 197 198 /** 199 * <p> 200 * Removes a child window. 201 * </p> 202 * 203 * @param child 204 * reference to the child window to be removed 205 */ 206 public void removeChild(WindowTreeNode child) { 207 children.remove(child); 208 } 209 210 /** 211 * <p> 212 * Adds a new child window and creates WindowTreeNode for it. 213 * </p> 214 * 215 * @param childHwnd 216 * hwnd of the child window 217 * @param childWindowName 218 * name of the child window 219 * @param resourceId 220 * resource id of the child window 221 * @param className 222 * class name of the child window 223 * @param isModal 224 * modality of the child window 225 * @return reference to the WindowTreeNode created for the child window 226 */ 227 public WindowTreeNode addChild(int childHwnd, String childWindowName, 228 int resourceId, String className, boolean isModal) { 229 WindowTreeNode child = new WindowTreeNode(childHwnd, this, 230 childWindowName, resourceId, className, isModal); 231 children.add(child); 232 return child; 233 } 234 235 /** 236 * <p> 237 * Returns a string identfier of the window:<br> 238 * {@code [resourceId;"windowName";"className";modality]} 239 * </p> 240 * 241 * @return identifier string of the window 242 */ 243 @Override 244 public String toString() { 245 return "[" + resourceId + ";\"" + windowName + "\";\"" + className 246 + "\";" + isModal + "]"; 247 } 248 249 /** 250 * <p> 251 * Returns an XML representation of the window, including its parents. It is 252 * defined as follows:<br> 253 * <code> 254 * parent#xmlRepresentation()<br> 255 * <window name="this.windowname" class="this.className" resourceId="this.resourceId" isModal="this.isModel"/> 256 * </code> 257 * </p> 258 * 259 * @return xml representation of the window 260 */ 261 public String xmlRepresentation() { 262 String xmlString = ""; 263 if (parent != null) { 264 xmlString = parent.xmlRepresentation(); 265 } 266 xmlString += "<window name=\"" 267 + StringTools.xmlEntityReplacement(windowName) + "\" class=\"" 268 + StringTools.xmlEntityReplacement(className) 269 + "\" resourceId=\"" + resourceId + "\" isModal=\"" + isModal 270 + "\" hwnd=\"" + hwnd + "\"" 271 + "/>"; 272 return xmlString; 273 } 274 275 /** 276 * <p> 277 * Returns the names of the parents and itself separated by dots, e.g., 278 * "GrandParent.Parent.windowName" 279 * </p> 280 * 281 * @return names of the parents separated by dots 282 */ 283 public String getParentNames() { 284 String parentNames = ""; 285 if (parent != null) { 286 parentNames = parent.getParentNames() + "."; 287 } 288 parentNames += windowName; 289 return parentNames; 290 } 23 public class MFCGUIElementSpec implements IGUIElementSpec { 24 25 /** 26 * <p> 27 * current name of the window 28 * </p> 29 */ 30 private String name; 31 32 /** 33 * <p> 34 * previous names of the window as it may have changed over time. 35 * </p> 36 */ 37 private List<String> formerNames = new ArrayList<String>(); 38 39 /** 40 * <p> 41 * Handle of the window. Used as unique identifier during its existence. 42 * </p> 43 */ 44 private long hwnd; 45 46 /** 47 * <p> 48 * previous handles of the window as the window may have been destroyed and recreated 49 * </p> 50 */ 51 private List<Long> formerHwnds = new ArrayList<Long>(); 52 53 /** 54 * <p> 55 * Resource id of the window. 56 * </p> 57 */ 58 private final int resourceId; 59 60 /** 61 * <p> 62 * type (class name) of the window. 63 * </p> 64 */ 65 private final String type; 66 67 /** 68 * <p> 69 * True, if the window is modal. 70 * </p> 71 */ 72 private final boolean isModal; 73 74 /** 75 * <p> 76 * Creates a new WindowTreeNode. 77 * </p> 78 * <p> 79 * The constructor is protected WindowTreeNode may only be created from the WindowTree. 80 * </p> 81 * 82 * @param hwnd 83 * hwnd of the window 84 * @param parent 85 * reference to the parent's WindowTreeNode 86 * @param name 87 * name of the window 88 * @param resourceId 89 * resource id of the window 90 * @param type 91 * type, i.e. class name of the window 92 * @param isModal 93 * modality of the window 94 */ 95 protected MFCGUIElementSpec(long hwnd, 96 String name, 97 int resourceId, 98 String type, 99 boolean isModal) 100 { 101 this.hwnd = hwnd; 102 this.name = name; 103 this.resourceId = resourceId; 104 this.type = type; 105 this.isModal = isModal; 106 } 107 108 /** 109 * <p> 110 * Returns the name of the window. 111 * </p> 112 * 113 * @return name of the window 114 */ 115 public String getName() { 116 StringBuffer names = new StringBuffer(); 117 118 if (name != null) { 119 names.append('"'); 120 names.append(name); 121 names.append('"'); 122 } 123 else { 124 names.append("NOT_SET"); 125 } 126 127 if (formerNames.size() > 0) { 128 129 names.append(" (aka "); 130 131 for (int i = 0; i < formerNames.size(); i++) { 132 if (i > 0) { 133 names.append("/"); 134 } 135 136 names.append('"'); 137 names.append(formerNames.get(i)); 138 names.append('"'); 139 } 140 141 names.append(")"); 142 } 143 144 return names.toString(); 145 } 146 147 /** 148 * <p> 149 * Returns the hwnd of the window. 150 * </p> 151 * 152 * @return hwnd of the window 153 */ 154 public long getHwnd() { 155 return hwnd; 156 } 157 158 /** 159 * <p> 160 * Returns the resource id of the window. 161 * </p> 162 * 163 * @return resource id of the window 164 */ 165 public int getResourceId() { 166 return resourceId; 167 } 168 169 /* (non-Javadoc) 170 * @see de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec#getType() 171 */ 172 @Override 173 public String getType() { 174 return type; 175 } 176 177 /** 178 * <p> 179 * TODO: comment 180 * </p> 181 * 182 * @return 183 */ 184 public boolean isModal() { 185 return isModal; 186 } 187 188 /** 189 * <p> 190 * Sets the name of the window. 191 * </p> 192 * 193 * @param text 194 * new name of the window 195 */ 196 public void setName(String newName) { 197 if ((this.name != null) && 198 (!this.name.equals(newName)) && 199 (!this.formerNames.contains(this.name))) 200 { 201 this.formerNames.add(this.name); 202 } 203 204 this.name = newName; 205 } 206 207 /** 208 * <p> 209 * Sets the hwnd of the window. 210 * </p> 211 * 212 * @param text 213 * new name of the window 214 */ 215 public void setHwnd(long newHwnd) { 216 if (!this.formerHwnds.contains(this.hwnd)) { 217 this.formerHwnds.add(this.hwnd); 218 } 219 220 this.hwnd = newHwnd; 221 } 222 223 /* (non-Javadoc) 224 * @see de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec#getSimilarity(de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec) 225 */ 226 @Override 227 public boolean getSimilarity(IGUIElementSpec other) { 228 229 if (this == other) { 230 return true; 231 } 232 233 if (!(other instanceof MFCGUIElementSpec)) { 234 return false; 235 } 236 237 MFCGUIElementSpec otherSpec = (MFCGUIElementSpec) other; 238 239 if ((type != otherSpec.type) && ((type != null) && (!type.equals(otherSpec.type)))) { 240 return false; 241 } 242 243 if (isModal != otherSpec.isModal) { 244 return false; 245 } 246 247 if (resourceId != otherSpec.resourceId) { 248 return false; 249 } 250 251 // up to now, we compared, if the basics match. Now lets compare the id and the 252 // name. Both may change. The name may be reset (e.g. the title of a frame using the 253 // asterisk in the case data was changed). The id may change if e.g. a dialog is closed 254 // and reopend, i.e. a new instance is created. If one of them stays the same, then 255 // similarity is given. Therefore these are the first two comparisons 256 257 if (hwnd == otherSpec.hwnd) { 258 return true; 259 } 260 261 if ((name != null) && (name.equals(otherSpec.name))) { 262 return true; 263 } 264 265 if ((((name == null) && (otherSpec.name == null)) || 266 (("".equals(name)) && ("".equals(otherSpec.name)))) && 267 (formerNames.size() == 0) && (otherSpec.formerNames.size() == 0)) 268 { 269 return true; 270 } 271 272 // if the hwnd and the name did not stay the same, then the name should be checked first. 273 // the current name of one of the specs must be contained in the former names of the 274 // respective other spec for similarity. Either of the specs should contain the name of the 275 // respective other spec in its former names. We can rely on this, as in the MFC context 276 // we get to know each name change. I.e. although currently the names of the specs differ, 277 // once they were identical. But it is sufficient to do it for the current names of the 278 // elements, as only one of them may have experienced more name changes then the other. 279 280 if ((otherSpec.name != null) && formerNames.contains(otherSpec.name)) 281 { 282 return true; 283 } 284 285 if ((name != null) && otherSpec.formerNames.contains(name)) { 286 return true; 287 } 288 289 // ok. Even the names to not match. This is usually a clear indication, that the elements 290 // are distinct. However, we check, if the former handles matched. This is very unlikely 291 // to happen. But it may occur, if a GUI element does not have a name or its name stays 292 // the empty string and if this GUI element is created, destroyed, and created again. 293 294 if (formerHwnds.contains(otherSpec.hwnd) || otherSpec.formerHwnds.contains(hwnd)) { 295 return true; 296 } 297 298 // now we can be really sure, that the GUI elements differ 299 300 return false; 301 } 302 303 /* (non-Javadoc) 304 * @see de.ugoe.cs.quest.eventcore.guimodel.IGUIElementSpec#equals(IGUIElementSpec) 305 */ 306 @Override 307 public boolean equals(IGUIElementSpec other) { 308 309 if (this == other) { 310 return true; 311 } 312 313 if (!(other instanceof MFCGUIElementSpec)) { 314 return false; 315 } 316 317 MFCGUIElementSpec otherSpec = (MFCGUIElementSpec) other; 318 319 return 320 (hwnd == otherSpec.hwnd) && (isModal == otherSpec.isModal) && 321 (resourceId == otherSpec.resourceId) && 322 ((type == otherSpec.type) || ((type != null) && (type.equals(otherSpec.type)))) && 323 ((name == otherSpec.name) || ((name != null) && (name.equals(otherSpec.name)))); 324 } 325 326 /* (non-Javadoc) 327 * @see java.lang.Object#hashCode() 328 */ 329 @Override 330 public int hashCode() { 331 // reuse only invariable elements 332 return (type + isModal + resourceId).hashCode(); 333 } 334 335 /** 336 * <p> 337 * Returns a string identfier of the window:<br> 338 * {@code [resourceId;"windowName";"className";modality]} 339 * </p> 340 * 341 * @return identifier string of the window 342 */ 343 @Override 344 public String toString() { 345 return "[" + resourceId + ";" + getName() + ";\"" + type + "\";" + isModal + ";" + 346 hwnd + "]"; 347 } 348 349 /** 350 * <p> 351 * TODO: comment 352 * </p> 353 */ 354 String toXML() { 355 return 356 "<window name=\"" + (name != null ? StringTools.xmlEntityReplacement(name) : "") + 357 "\" class=\"" + StringTools.xmlEntityReplacement(type) + 358 "\" resourceId=\"" + resourceId + "\" isModal=\"" + 359 isModal + "\" hwnd=\"" + hwnd + "\"" + "/>"; 360 } 361 362 /** 363 * <p> 364 * TODO: comment 365 * </p> 366 * 367 * @param furtherSpec 368 */ 369 void update(IGUIElementSpec furtherSpec) { 370 MFCGUIElementSpec other = (MFCGUIElementSpec) furtherSpec; 371 372 if (other != this) { 373 for (long formerHwnd : other.formerHwnds) { 374 setHwnd(formerHwnd); 375 } 376 377 if (hwnd != other.hwnd) { 378 hwnd = other.hwnd; 379 } 380 381 for (String formerName : other.formerNames) { 382 setName(formerName); 383 } 384 385 if ((name != other.name) && (name != null) && (!name.equals(other.name))) 386 { 387 setName(other.name); 388 } 389 } 390 } 291 391 292 392 } -
trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/guimodel/WindowTree.java
r578 r619 1 package de.ugoe.cs.quest.plugin.mfc.eventcore; 2 1 2 package de.ugoe.cs.quest.plugin.mfc.guimodel; 3 4 import java.util.ArrayList; 3 5 import java.util.HashMap; 6 import java.util.HashSet; 4 7 import java.util.List; 5 8 import java.util.Map; 6 import java.util.SortedSet; 7 import java.util.TreeSet; 9 import java.util.Set; 10 11 import de.ugoe.cs.quest.eventcore.guimodel.GUIElementFactory; 12 import de.ugoe.cs.quest.eventcore.guimodel.GUIModel; 13 import de.ugoe.cs.quest.eventcore.guimodel.GUIModelException; 14 import de.ugoe.cs.quest.eventcore.guimodel.IGUIElementFactory; 15 8 16 9 17 /** … … 12 20 * </p> 13 21 * <p> 14 * The window tree represents the hierarchical structure of the windows 15 * "as it is" currently during a session. It may change during the session due 16 * to creation and destruction of windows. 17 * </p> 18 * <p> 19 * The class is implemented as a singleton. The rational behind implementing 20 * this class as a singleton is to ease the access of all class that may request 21 * information about the windows during the parsing of a session. As the tree 22 * may change during the session, it does not make sense to preserve it after a 23 * session. Thus, it can just be deleted. Therefore, as long as only one session 24 * is parsed at a time, a single instance is sufficient. 22 * The window tree represents the hierarchical structure of the windows "as it is" currently during 23 * a session. It may change during the session due to creation and destruction of windows. 25 24 * </p> 26 25 * … … 30 29 public class WindowTree { 31 30 32 /** 33 * <p> 34 * Handle to the window instance. 35 * </p> 36 */ 37 private static WindowTree theInstance = null; 38 39 /** 40 * <p> 41 * Maintains a set of all the target strings of all widgets that were at 42 * some point part of the window tree. 43 * </p> 44 */ 45 private SortedSet<String> targets; 46 47 /** 48 * <p> 49 * Obtain a handle to the window instance. 50 * </p> 51 * 52 * @return instance of the window tree 53 */ 54 public static WindowTree getInstance() { 55 if (theInstance == null) { 56 theInstance = new WindowTree(); 57 } 58 return theInstance; 59 } 60 61 /** 62 * <p> 63 * Resets the tree. Should be used between sessions. 64 * </p> 65 */ 66 public static void resetTree() { 67 theInstance = null; 68 } 69 70 /** 71 * <p> 72 * Map of all windows that are part of the tree for efficient searching. The 73 * keys of the map are the hwnd's of the windows. 74 * </p> 75 */ 76 private Map<Integer, WindowTreeNode> nodes; 77 78 /** 79 * <p> 80 * Creates a new WindowTree. 81 * </p> 82 * <p> 83 * Private, as the class is a singleton. 84 * </p> 85 */ 86 private WindowTree() { 87 nodes = new HashMap<Integer, WindowTreeNode>(); 88 targets = new TreeSet<String>(); 89 } 90 91 /** 92 * <p> 93 * Adds a new window to the tree. 94 * </p> 95 * 96 * @param parentHwnd 97 * hwnd of the parent window 98 * @param childHwnd 99 * hwnd of the window to be created 100 * @param childWindowName 101 * resource id of the window to be created 102 * @param resourceId 103 * resource id of the window to be created 104 * @param className 105 * class name of the window to be created 106 */ 107 public void add(int parentHwnd, int childHwnd, String childWindowName, 108 int resourceId, String className, boolean isModal) { 109 WindowTreeNode parent = nodes.get(parentHwnd); 110 WindowTreeNode child = nodes.get(childHwnd); 111 if (child == null) { 112 if (parent != null) { 113 child = parent.addChild(childHwnd, childWindowName, resourceId, 114 className, isModal); 115 } else { 116 child = new WindowTreeNode(childHwnd, null, childWindowName, 117 resourceId, className, isModal); 118 } 119 nodes.put(childHwnd, child); 120 targets.add(child.xmlRepresentation()); 121 } 122 } 123 124 /** 125 * <p> 126 * Removes a window (defined by its hwnd) from the tree. All children of the 127 * window will be removed recursively. 128 * </p> 129 * 130 * @param hwnd 131 * hwnd of the window to be removed 132 * @return number of windows that were removed 133 */ 134 public int remove(int hwnd) { 135 int removedCounter = 0; 136 WindowTreeNode node = nodes.get(hwnd); 137 if (node != null) { 138 List<WindowTreeNode> nodesToBeRemoved = node.remove(); 139 for (int i = 0; i < nodesToBeRemoved.size(); i++) { 140 WindowTreeNode nodeToBeRemoved = nodesToBeRemoved.get(i); 141 nodesToBeRemoved.addAll(nodeToBeRemoved.getChildren()); 142 nodes.remove(nodeToBeRemoved.getHwnd()); 143 removedCounter++; 144 } 145 nodes.remove(hwnd); 146 removedCounter++; 147 } 148 return removedCounter; 149 } 150 151 /** 152 * <p> 153 * Searches the tree for a window with the specified hwnd and returns its 154 * {@link WindowTreeNode}. 155 * </p> 156 * 157 * @param hwnd 158 * hwnd that is looked for 159 * @return {@link WindowTreeNode} of the window with the given hwnd if 160 * found, null otherwise 161 */ 162 public WindowTreeNode find(int hwnd) { 163 return nodes.get(hwnd); 164 } 165 166 /** 167 * <p> 168 * Returns the number of nodes contained in the WindowTree. 169 * </p> 170 * 171 * @return number of nodes 172 */ 173 public int size() { 174 return nodes.size(); 175 } 176 177 /** 178 * <p> 179 * Returns a sorted set of all targets that existed any time in the window 180 * tree. 181 * </p> 182 * 183 * @return set of targets 184 */ 185 public SortedSet<String> getTargets() { 186 return targets; 187 } 31 /** 32 * <p> 33 * Maintains a set of all the targets of all widgets that were at some point part of the 34 * window tree. 35 * </p> 36 */ 37 private Set<MFCGUIElementSpec> targets; 38 39 /** 40 * <p> 41 * Map of all GUI element specifications that are part of the tree for efficient searching. 42 * The keys of the map are the hwnd's of the GUI elements. 43 * </p> 44 */ 45 private Map<Long, MFCGUIElementSpec> guiElementSpecs; 46 47 /** 48 * <p> 49 * Map of all children of GUI elements that are part of the tree. The keys of the map are 50 * the hwnd's of the parent GUI elements. 51 * </p> 52 */ 53 private Map<Long, List<MFCGUIElementSpec>> childRelations; 54 55 /** 56 * <p> 57 * Map of all parents of GUI elements that are part of the tree. The keys of the map are 58 * the hwnd's of the child GUI elements. 59 * </p> 60 */ 61 private Map<Long, MFCGUIElementSpec> parentRelations; 62 63 /** 64 * <p> 65 * the internally created GUI model 66 * </p> 67 */ 68 private GUIModel guiModel = new GUIModel(); 69 70 /** 71 * <p> 72 * the GUI element factory used in the model 73 * </p> 74 */ 75 private IGUIElementFactory guiElementFactory = GUIElementFactory.getInstance(); 76 77 /** 78 * <p> 79 * Map of all GUI elements that are part of the tree for efficient searching. The keys of the 80 * map are the hwnd's of the GUI elements. 81 * </p> 82 */ 83 private Map<Long, MFCGUIElement> guiElements; 84 85 /** 86 * <p> 87 * Creates a new WindowTree. 88 * </p> 89 * <p> 90 * Private, as the class is a singleton. 91 * </p> 92 */ 93 public WindowTree() { 94 guiElementSpecs = new HashMap<Long, MFCGUIElementSpec>(); 95 targets = new HashSet<MFCGUIElementSpec>(); 96 childRelations = new HashMap<Long, List<MFCGUIElementSpec>>(); 97 parentRelations = new HashMap<Long, MFCGUIElementSpec>(); 98 guiElements = new HashMap<Long, MFCGUIElement>(); 99 } 100 101 /** 102 * <p> 103 * Adds a new window to the tree. 104 * </p> 105 * 106 * @param parentHwnd 107 * hwnd of the parent window 108 * @param childHwnd 109 * hwnd of the window to be created 110 * @param childWindowName 111 * resource id of the window to be created 112 * @param resourceId 113 * resource id of the window to be created 114 * @param className 115 * class name of the window to be created 116 */ 117 public void add(long parentHwnd, 118 long childHwnd, 119 String childWindowName, 120 int resourceId, 121 String className, 122 boolean isModal) 123 { 124 MFCGUIElementSpec parent = guiElementSpecs.get(parentHwnd); 125 MFCGUIElementSpec child = guiElementSpecs.get(childHwnd); 126 if (child == null) { 127 child = 128 new MFCGUIElementSpec(childHwnd, childWindowName, resourceId, className, isModal); 129 if (parent != null) { 130 List<MFCGUIElementSpec> otherChildren = childRelations.get(parentHwnd); 131 132 if (otherChildren == null) { 133 otherChildren = new ArrayList<MFCGUIElementSpec>(); 134 childRelations.put(parentHwnd, otherChildren); 135 } 136 137 otherChildren.add(child); 138 139 parentRelations.put(childHwnd, parent); 140 } 141 guiElementSpecs.put(childHwnd, child); 142 targets.add(child); 143 } 144 } 145 146 /** 147 * <p> 148 * Searches the tree for a window with the specified hwnd and returns its {@link MFCGUIElementSpec} 149 * . 150 * </p> 151 * 152 * @param hwnd 153 * hwnd that is looked for 154 * @return {@link MFCGUIElementSpec} of the window with the given hwnd if found, null otherwise 155 */ 156 public MFCGUIElement find(long hwnd) { 157 MFCGUIElement guiElement = guiElements.get(hwnd); 158 if (guiElement == null) { 159 List<MFCGUIElementSpec> guiElementPath = new ArrayList<MFCGUIElementSpec>(); 160 161 MFCGUIElementSpec child = guiElementSpecs.get(hwnd); 162 163 if (child == null) { 164 throw new RuntimeException("no GUI element found with id " + hwnd); 165 } 166 167 while (child != null) { 168 guiElementPath.add(0, child); 169 child = parentRelations.get(child.getHwnd()); 170 } 171 172 try { 173 guiElement = (MFCGUIElement) 174 guiModel.integratePath(guiElementPath, guiElementFactory); 175 } 176 catch (GUIModelException e) { 177 throw new RuntimeException("could not instantiate GUI element with id " + hwnd, e); 178 } 179 guiElements.put(hwnd, guiElement); 180 } 181 return guiElement; 182 } 183 184 /** 185 * <p> 186 * TODO: comment 187 * </p> 188 * 189 * @param hwnd 190 * @param windowName 191 */ 192 public void setName(long hwnd, String windowName) { 193 MFCGUIElementSpec child = guiElementSpecs.get(hwnd); 194 if (child != null) { 195 child.setName(windowName); 196 197 MFCGUIElement guiElement = guiElements.remove(hwnd); 198 if (guiElement == null) { 199 // we need to update the GUI model as well 200 find(hwnd); 201 } 202 } 203 } 204 205 /** 206 * <p> 207 * Removes a window (defined by its hwnd) from the tree. All children of the window will be 208 * removed recursively. 209 * </p> 210 * 211 * @param hwnd 212 * hwnd of the window to be removed 213 * @return number of windows that were removed 214 */ 215 public int remove(long hwnd) { 216 MFCGUIElementSpec node = guiElementSpecs.remove(hwnd); 217 int removedCounter = 1; 218 219 if (node != null) { 220 List<MFCGUIElementSpec> nodesToBeRemoved = childRelations.remove(hwnd); 221 222 // remove all children and sub-children, if any 223 if (nodesToBeRemoved != null) { 224 for (int i = 0; i < nodesToBeRemoved.size(); i++) { 225 MFCGUIElementSpec nodeToBeRemoved = nodesToBeRemoved.get(i); 226 List<MFCGUIElementSpec> children = 227 childRelations.remove(nodeToBeRemoved.getHwnd()); 228 229 if (children != null) { 230 nodesToBeRemoved.addAll(children); 231 } 232 233 guiElementSpecs.remove(nodeToBeRemoved.getHwnd()); 234 parentRelations.remove(nodeToBeRemoved.getHwnd()); 235 removedCounter++; 236 } 237 } 238 239 // the node may be a child node of a parent. So search for it and remove it 240 MFCGUIElementSpec parent = parentRelations.remove(hwnd); 241 if (parent != null) { 242 List<MFCGUIElementSpec> children = childRelations.get(parent.getHwnd()); 243 244 if (children != null) { 245 for (int i = 0; i < children.size(); i++) { 246 if (children.get(i).getHwnd() == hwnd) { 247 children.remove(i); 248 break; 249 } 250 } 251 252 if (children.size() <= 0) { 253 childRelations.remove(parent.getHwnd()); 254 } 255 } 256 } 257 } 258 return removedCounter; 259 } 260 261 /** 262 * @return the guiModel 263 */ 264 public GUIModel getGUIModel() { 265 return guiModel; 266 } 267 268 /** 269 * <p> 270 * Returns the number of nodes contained in the WindowTree. 271 * </p> 272 * 273 * @return number of nodes 274 */ 275 public int size() { 276 return guiElementSpecs.size(); 277 } 278 279 /** 280 * <p> 281 * Returns a sorted set of all targets that existed any time in the window tree. 282 * </p> 283 * 284 * @return set of targets 285 */ 286 public Set<MFCGUIElementSpec> getTargets() { 287 return targets; 288 } 289 188 290 }
Note: See TracChangeset
for help on using the changeset viewer.