Changeset 619 for trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/EventGenerator.java
- Timestamp:
- 08/27/12 11:45:09 (12 years ago)
- File:
-
- 1 edited
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 }
Note: See TracChangeset
for help on using the changeset viewer.