Ignore:
Timestamp:
08/27/12 11:45:09 (12 years ago)
Author:
pharms
Message:
  • adapted implementation to now generate a full GUI model as well as concrete GUI interaction events
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 
    12package de.ugoe.cs.quest.plugin.mfc; 
    23 
    34import java.io.IOException; 
    45import java.security.InvalidParameterException; 
     6import java.util.ArrayList; 
    57import java.util.HashMap; 
    68import java.util.Iterator; 
     
    1820 
    1921import 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; 
     22import de.ugoe.cs.quest.eventcore.IEventType; 
     23import de.ugoe.cs.quest.plugin.mfc.EventGenerationRule.Term; 
     24import de.ugoe.cs.quest.plugin.mfc.eventcore.MFCEvent; 
     25import de.ugoe.cs.quest.plugin.mfc.eventcore.MFCEventTypeFactory; 
     26import de.ugoe.cs.quest.plugin.mfc.eventcore.ReplayWindowsMessage; 
    2427import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessage; 
     28import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessageType; 
     29import de.ugoe.cs.quest.plugin.mfc.guimodel.MFCGUIElement; 
     30import de.ugoe.cs.quest.plugin.mfc.guimodel.WindowTree; 
    2531import de.ugoe.cs.util.console.Console; 
    2632 
    2733/** 
    2834 * <p> 
    29  * Translates sequences of windows messages into {@link WindowsEvent}s that can 
    30  * be used by the EventBench core libraries. 
     35 * Translates sequences of windows messages into {@link WindowsEvent}s that can be used by the 
     36 * EventBench core libraries. 
    3137 * </p> 
    3238 *  
     
    3642public class EventGenerator { 
    3743 
    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    } 
    10001109 
    10011110} 
Note: See TracChangeset for help on using the changeset viewer.