package de.ugoe.cs.quest.plugin.mfc; import java.io.IOException; import java.security.InvalidParameterException; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.NoSuchElementException; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.Namespace; import org.jdom.input.SAXBuilder; import de.ugoe.cs.quest.eventcore.Event; import de.ugoe.cs.quest.plugin.mfc.eventcore.MFCEventTarget; import de.ugoe.cs.quest.plugin.mfc.eventcore.MFCEventType; import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowTree; import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowTreeNode; import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessage; import de.ugoe.cs.util.console.Console; /** *

* Translates sequences of windows messages into {@link WindowsEvent}s that can * be used by the EventBench core libraries. *

* * @author Steffen Herbold * @version 1.0 */ public class EventGenerator { /** *

* Helper method that fetches the document node of an XML file. *

* * @param filename * name of the XML file * @return the document node */ private static Document getDocument(String filename) { SAXBuilder builder = new SAXBuilder(); Document doc = null; try { doc = builder.build(filename); rulesNamespace = Namespace.getNamespace("ul:rules"); } catch (JDOMException e) { Console.printerrln("Invalid rules file."); e.printStackTrace(); } catch (IOException e) { Console.printerrln("Invalid rules file."); e.printStackTrace(); } return doc; } /** *

* Name and path of the XML files containing the rules. *

*/ private String rulesFile; /** *

* Iterator used for the current sequence. *

*/ private ListIterator sequenceIterator; /** *

* Token that is currently being generated. *

*/ private Event currentToken; /** *

* Event type of the current token. Stored as a member to be able to update it during the parsing of the idinfo tag. *

*/ private MFCEventType currentType; /** *

* Reference to the ul:rules namespace. *

*/ private static Namespace rulesNamespace; /** *

* The name of the rule that is currently being evaluated. *

*/ private String currentRuleName; /** *

* Internal message storage. Used to implement the * {@literal } and {@literal } * tags. *

*/ private Map messageStorage; /** *

* Creates a new EventGenerator. Sets "rules/rules.xml" as default file for * the rules. *

*/ public EventGenerator() { rulesFile = "rules/rules.xml"; } /** *

* Tries to match the rules to the given sequence to generate an * {@link WindowsEvent}. *

*

* The rules are matched the order, in which they are defined in the XML * file. Therefore, the order of the rules in the file defines priorities, * when multiple rules could be matched to the same sequence. *

* * @param sequence * sequence of message for which an event will be generated * @return event that matches the messages; null, if no rule can be matched */ @SuppressWarnings("unchecked") public Event generateEvent(List sequence) { Document rulesDoc = getDocument(rulesFile); Element rulesRoot = rulesDoc.getRootElement(); List ruleElements = rulesRoot.getChildren("rule", rulesNamespace); boolean isMatch = false; for (int ruleIndex = 0; ruleIndex < ruleElements.size() && !isMatch; ruleIndex++) { Element currentRule = ruleElements.get(ruleIndex); currentRuleName = currentRule.getAttributeValue("name"); currentType = new MFCEventType(currentRuleName); currentToken = new Event(currentType); isMatch = true; messageStorage = new HashMap(); sequenceIterator = sequence.listIterator(); List ruleChildrenMsg = currentRule.getChildren("msg", rulesNamespace); int i = 0; while (isMatch && i < ruleChildrenMsg.size()) { Element messageElement = ruleChildrenMsg.get(i); if ("true".equals(messageElement.getAttributeValue("multiple"))) { Element nextMessageElement = null; if (i + 1 < ruleChildrenMsg.size()) { nextMessageElement = ruleChildrenMsg.get(i + 1); } try { isMatch = matchMultipleMessages(messageElement, nextMessageElement); } catch (InvalidParameterException e) { Console.printerrln(e.getMessage()); } } else { try { isMatch = matchSingleMessage(messageElement); } catch (InvalidParameterException e) { Console.printerrln(e.getMessage()); } } i++; } if (isMatch) { List ruleChildren = currentRule.getChildren(); for (Element genMsgElement : ruleChildren) { if (genMsgElement.getName().equals("genMsg")) { try { generateReplayMessage(genMsgElement); } catch (InvalidParameterException e) { Console.printerrln(e.getMessage()); // TODO currentToken.invalidateReplay(); } } else if (genMsgElement.getName().equals("genMsgSeq")) { try { generateReplaySequence(genMsgElement); // TODO currentToken.invalidateReplay(); } catch (InvalidParameterException e) { Console.printerrln(e.getMessage()); // TODO currentToken.invalidateReplay(); } } } Element idinfoElement = currentRule.getChild("idinfo", rulesNamespace); if (idinfoElement != null) { // cannot be empty if document is valid List valueElements = idinfoElement.getChildren(); currentType.setInfo(getTermValue(null, valueElements.get(0))); } Console.traceln(currentToken.getType().toString() + " matched"); } else { currentToken = null; } } if (!isMatch) { Console.traceln("no match found for sequence: " + sequence.toString()); } return currentToken; } // //////////////////////////////////////////////////////////// // Helper functions for matching of events, i.e., msg-nodes // // //////////////////////////////////////////////////////////// /** *

* Handles msg-nodes where multiple is not true, i.e., not a sequences. *

* * @param messageElement * {@link Element} representing the msg-node * @return true, if a match is found; false otherwise */ private boolean matchSingleMessage(Element messageElement) { boolean isMatch = false; WindowsMessage currentMessage = null; int type = Integer.parseInt(messageElement.getAttributeValue("type")); while (!isMatch && sequenceIterator.hasNext()) { /* * traverses the messages from the current position forward till a * message with the correct type is found */ currentMessage = sequenceIterator.next(); if (type == currentMessage.getType()) { // message with the correct type found // eval child nodes for further matching/storing isMatch = evalEqualRestrictions(currentMessage, messageElement); // in case the message is a match, eval storage children if (isMatch) { handleStorage(messageElement, currentMessage); currentToken.setTarget(new MFCEventTarget(currentMessage .getXmlWindowDescription())); // TODO currentToken.setTargetShort(currentMessage.getParentNames()); } } } return isMatch; } /** *

* Handles msg-nodes where multiple is true, i.e., sequences. Requires * knowledge about the next msg-node to determine the end of the sequence. *

* * @param messageElement * {@link Element} representing the msg-node * @param nextMessageElement * {@link Element} representing the next msg-node; {@code null} * if the current node is the last one * @return true, if a sequence is matched; false otherwise */ private boolean matchMultipleMessages(Element messageElement, Element nextMessageElement) { boolean isMatch = false; boolean isCurrentMatch = false; boolean nextMatchFound = false; WindowsMessage currentMessage = null; WindowsMessage nextMessage = null; int type = Integer.parseInt(messageElement.getAttributeValue("type")); int nextType = -1; if (nextMessageElement != null) { nextType = Integer.parseInt(nextMessageElement .getAttributeValue("type")); } while (!nextMatchFound && sequenceIterator.hasNext()) { currentMessage = sequenceIterator.next(); if (type == currentMessage.getType()) { isCurrentMatch = evalEqualRestrictions(currentMessage, messageElement); isMatch = isMatch || isCurrentMatch; if (isCurrentMatch) { handleStorage(messageElement, currentMessage); currentToken.setTarget(new MFCEventTarget(currentMessage .getXmlWindowDescription())); // TODO currentToken.setTargetShort(currentMessage.getParentNames()); } } if (nextMessageElement != null && isMatch) { // peek next message to check if the sequence ends and the next // match is found if (!sequenceIterator.hasNext()) { return false; // sequence is over, but not all messages are // found } nextMessage = sequenceIterator.next(); sequenceIterator.previous(); if (nextType == nextMessage.getType()) { nextMatchFound = evalEqualRestrictions(nextMessage, nextMessageElement); } } } return isMatch; } /** *

* Handles equals-nodes. *

* * @param currentMessage * {@link Element} representing the msg-node the equals-node * belongs to * @param messageElement * {@link Element} representing the equals-node to be evaluated * @return true, if constraint is fulfilled; false otherwise */ @SuppressWarnings("unchecked") private boolean evalEqualRestrictions(WindowsMessage currentMessage, Element messageElement) { boolean isMatch = true; for (Element childElement : (List) messageElement.getChildren( "equals", rulesNamespace)) { List termElements = childElement.getChildren(); // the size 2 of termElements is guaranteed by the XML schema String value1 = getTermValue(currentMessage, termElements.get(0)); String value2 = getTermValue(currentMessage, termElements.get(1)); if (value1 == null || value2 == null) { isMatch = false; } else { isMatch = isMatch && value1.equals(value2); } } for (Element childElement : (List) messageElement.getChildren( "equalsSeq", rulesNamespace)) { List termElements = childElement.getChildren(); List values1 = getTermValueSeq(termElements.get(0)); List values2 = getTermValueSeq(termElements.get(0)); if (values1 == null || values2 == null) { isMatch = false; } else { isMatch = isMatch && values1.equals(values2); } } return isMatch; } /** *

* Handles store-nodes and storeSeq-nodes. *

* * @param messageElement * {@link Element} representing the msg-node that is currently * being evaluated * @param currentMessage * current message in the message sequence that is matched; this * is the message that is stored */ @SuppressWarnings("unchecked") private void handleStorage(Element messageElement, WindowsMessage currentMessage) { for (Element childElement : (List) messageElement.getChildren( "store", rulesNamespace)) { String identifier = childElement.getAttributeValue("var"); messageStorage.put(identifier, currentMessage); resolveHwnd(currentMessage, childElement); } for (Element childElement : (List) messageElement.getChildren( "storeSeq", rulesNamespace)) { String identifier = childElement.getAttributeValue("varSeq"); Object tmp = messageStorage.get(identifier); List storedSequence; if (tmp == null || tmp instanceof WindowsMessage) { storedSequence = new LinkedList(); storedSequence.add(currentMessage); messageStorage.put(identifier, storedSequence); } else if (tmp instanceof List) { storedSequence = (List) tmp; storedSequence.add(currentMessage); messageStorage.put(identifier, storedSequence); } resolveHwnd(currentMessage, childElement); } } /** *

* Resolves a parameter that contains a HWND of a message to the target * string of the HWND and stores it. *

* * @param currentMessage * message whose HWND is resolved * @param childElement * child element of the store node that represents the resolve */ @SuppressWarnings("unchecked") private void resolveHwnd(WindowsMessage currentMessage, Element childElement) { List resolveElements = childElement.getChildren("resolveHwnd", rulesNamespace); for (Element resolveElement : resolveElements) { String param = resolveElement.getAttributeValue("param"); String storeParam = resolveElement.getAttributeValue("storeParam"); int paramHwnd = Integer .parseInt(currentMessage.getParameter(param)); WindowTreeNode node = WindowTree.getInstance().find(paramHwnd); if (node != null) { currentMessage.addParameter(storeParam, node.xmlRepresentation()); } } } // ///////////////////////////////////////////////////// // Helper functions for generating the replay, i.e., // parsing of genMsg und genMsgSeq-nodes // ///////////////////////////////////////////////////// /** *

* Handles genMsg-nodes and adds the replay to the {@link Event} that is * generated. *

* * @param genMsgElement * {@link Element} representing the genMsg-node */ @SuppressWarnings("unchecked") private void generateReplayMessage(Element genMsgElement) { List genMsgChildren = genMsgElement.getChildren(); WindowsMessage generatedMessage = null; if (genMsgChildren.size() == 1) { // replay stored message without // change String obj = genMsgChildren.get(0).getAttributeValue("obj"); generatedMessage = getStoredMessageVariable(null, obj); } else { // generate message according to the rule for (Element genMsgChild : genMsgChildren) { Element termElement = (Element) genMsgChild.getChildren() .get(0); if (genMsgChild.getName().equals("type")) { try { int msgType = Integer.parseInt(getTermValue(null, termElement)); generatedMessage = new WindowsMessage(msgType); } catch (NumberFormatException e) { throw new InvalidParameterException( "Failure generating replay sequence for rule " + currentRuleName + ": Defined type is not an integer."); } } else if (genMsgChild.getName().equals("target")) { String targetString = getTermValue(null, termElement); generatedMessage.setXmlWindowDescription(targetString); } else if (genMsgChild.getName().equals("LPARAM")) { String paramValueStr = getTermValue(null, termElement); long paramValue = 0; Element loword = genMsgChild.getChild("LOWORD", rulesNamespace); if (loword != null) { paramValue = loHiWord(genMsgChild); generatedMessage.setLPARAM(paramValue); } else { try { paramValue = Integer.parseInt(paramValueStr); generatedMessage.setLPARAM(paramValue); } catch (NumberFormatException e) { generatedMessage .setLPARAMasWindowDesc(paramValueStr); } } } else if (genMsgChild.getName().equals("WPARAM")) { String paramValueStr = getTermValue(null, termElement); long paramValue = 0; Element loword = genMsgChild.getChild("LOWORD", rulesNamespace); if (loword != null) { paramValue = loHiWord(genMsgChild); generatedMessage.setWPARAM(paramValue); } else { try { paramValue = Integer.parseInt(paramValueStr); generatedMessage.setWPARAM(paramValue); } catch (NumberFormatException e) { generatedMessage .setWPARAMasWindowDesc(paramValueStr); } } } } } if (generatedMessage != null) { int delay = Integer.parseInt(genMsgElement .getAttributeValue("delay")); generatedMessage.setDelay(delay); } else { // TODO currentToken.invalidateReplay(); } currentToken.addReplayable(generatedMessage); } /** * Handles genMsgSeq-nodes and adds the replay to the {@link Event} that is * generated.

* * @param genMsgElement * {@link Element} representing the genMsgSeq-node. */ @SuppressWarnings("unchecked") private void generateReplaySequence(Element genMsgElement) { List genMsgSeqChildren = genMsgElement.getChildren(); List generatedMessageSeq = new LinkedList(); if (genMsgSeqChildren.size() == 1) { String obj = genMsgSeqChildren.get(0).getAttributeValue("seqObj"); generatedMessageSeq = getStoredSeqVariable(obj); } else { boolean msgsGenerated = false; int constMsgType = 0; for (Element genMsgSeqChild : genMsgSeqChildren) { Element termElement = (Element) genMsgSeqChild.getChildren() .get(0); if (genMsgSeqChild.getName().equals("type")) { // note: cannot easily be extracted because of mulitple // return values if (termElement.getName().equals("seqValue")) { String obj = termElement.getAttributeValue("seqObj"); List seqVar = getStoredSeqVariable(obj); for (WindowsMessage msg : seqVar) { generatedMessageSeq.add(new WindowsMessage(msg .getType())); } msgsGenerated = true; } else { // constValue type constMsgType = Integer.parseInt(getTermValue(null, termElement)); } } else if (genMsgSeqChild.getName().equals("target")) { msgsGenerated = createSequenceTarget(generatedMessageSeq, msgsGenerated, constMsgType, termElement); } else if (genMsgSeqChild.getName().equals("LPARAM")) { msgsGenerated = createSequenceLParam(generatedMessageSeq, msgsGenerated, constMsgType, termElement); } else if (genMsgSeqChild.getName().equals("WPARAM")) { msgsGenerated = createSequenceWParam(generatedMessageSeq, msgsGenerated, constMsgType, termElement); } } } currentToken.addReplayableSequence(generatedMessageSeq); } /** *

* Creates the targets for replay sequences generated with genMsgSeq-nodes. *

* * @param generatedMessageSeq * list of the messages that is being generated * @param msgsGenerated * boolean stating if the list of messages is already generated * or if the generation has to be handles by this method * @param constMsgType * a constant message type that is used for message generation, * in case the list of message is generated by this method * @param termElement * {@link Element} representing the term-node describing the * target * @return true, if the list of message is generated after calling this * method; false otherwise * @throws NoSuchElementException * thrown if the seqVar referred to in the termElement contains * a different number of messages than is contained in * messageSeq */ private boolean createSequenceTarget( List generatedMessageSeq, boolean msgsGenerated, int constMsgType, Element termElement) throws NoSuchElementException { Iterator seqIterator = generatedMessageSeq.iterator(); if (termElement.getName().equals("seqValue")) { String obj = termElement.getAttributeValue("seqObj"); List seqVar = getStoredSeqVariable(obj); if (msgsGenerated && seqVar.size() != generatedMessageSeq.size()) { throw new InvalidParameterException( "Failure generating replay sequence for rule " + currentRuleName + ": One or more of the sequence variables used to generate a sequence have different lenghts."); } for (WindowsMessage msg : seqVar) { WindowsMessage currentSeqMsg = getCurrentSeqMsg( generatedMessageSeq, msgsGenerated, constMsgType, seqIterator); String targetString = msg.getParameter(termElement .getAttributeValue("param")); currentSeqMsg.setXmlWindowDescription(targetString); } msgsGenerated = true; } else { // const value throw new AssertionError("target must be a sequence variable!"); /* * If target would not be a variable, the message-elements could not * yet be created and the whole sequence might be broken. If this is * to be changed, createSequenceLParam and createSequenceWParam need * to be addepted, too. */ } return msgsGenerated; } /** *

* Creates the LPARAMs for replay sequences generated with genMsgSeq-nodes. *

* * @param generatedMessageSeq * list of the messages that is being generated * @param msgsGenerated * boolean stating if the list of messages is already generated * or if the generation has to be handles by this method * @param constMsgType * a constant message type that is used for message generation, * in case the list of message is generated by this method * @param termElement * {@link Element} representing the term-node describing the * LPARAM * @return true, if the list of message is generated after calling this * method; false otherwise * @throws NoSuchElementException * thrown if the seqVar referred to in the termElement contains * a different number of messages than is contained in * messageSeq */ private boolean createSequenceLParam( List generatedMessageSeq, boolean msgsGenerated, int constMsgType, Element termElement) throws NoSuchElementException { Iterator seqIterator = generatedMessageSeq.iterator(); if (termElement.getName().equals("seqValue")) { String obj = termElement.getAttributeValue("seqObj"); List seqVar = getStoredSeqVariable(obj); if (msgsGenerated && seqVar.size() != generatedMessageSeq.size()) { throw new InvalidParameterException( "Failure generating replay sequence for rule " + currentRuleName + ": One or more of the sequence variables used to generate a sequence have different lenghts."); } for (WindowsMessage msg : seqVar) { WindowsMessage currentSeqMsg = getCurrentSeqMsg( generatedMessageSeq, msgsGenerated, constMsgType, seqIterator); String paramValueStr = msg.getParameter(termElement .getAttributeValue("param")); int paramValue = 0; try { paramValue = Integer.parseInt(paramValueStr); currentSeqMsg.setLPARAM(paramValue); } catch (NumberFormatException e) { currentSeqMsg.setLPARAMasWindowDesc(paramValueStr); } } if (seqIterator.hasNext()) { // the first seq-var has a different number of elements than the // current one throw new NoSuchElementException(); } msgsGenerated = true; } else { // const value int paramValue = Integer.parseInt(getTermValue(null, termElement)); while (seqIterator.hasNext()) { seqIterator.next().setLPARAM(paramValue); } } return msgsGenerated; } /** *

* Creates the WPARAMs for replay sequences generated with genMsgSeq-nodes. *

* * @param generatedMessageSeq * list of the messages that is being generated * @param msgsGenerated * boolean stating if the list of messages is already generated * or if the generation has to be handles by this method * @param constMsgType * a constant message type that is used for message generation, * in case the list of message is generated by this method * @param termElement * {@link Element} representing the term-node describing the * WPARAM * @return true, if the list of message is generated after calling this * method; false otherwise * @throws NoSuchElementException * thrown if the seqVar referred to in the termElement contains * a different number of messages than is contained in * messageSeq */ private boolean createSequenceWParam( List generatedMessageSeq, boolean msgsGenerated, int constMsgType, Element termElement) throws NoSuchElementException { Iterator seqIterator = generatedMessageSeq.iterator(); if (termElement.getName().equals("seqValue")) { String obj = termElement.getAttributeValue("seqObj"); List seqVar = getStoredSeqVariable(obj); if (msgsGenerated && seqVar.size() != generatedMessageSeq.size()) { throw new InvalidParameterException( "Failure generating replay sequence for rule " + currentRuleName + ": One or more of the sequence variables used to generate a sequence have different lenghts."); } for (WindowsMessage msg : seqVar) { WindowsMessage currentSeqMsg = getCurrentSeqMsg( generatedMessageSeq, msgsGenerated, constMsgType, seqIterator); String paramValueStr = msg.getParameter(termElement .getAttributeValue("param")); int paramValue = 0; try { paramValue = Integer.parseInt(paramValueStr); currentSeqMsg.setWPARAM(paramValue); } catch (NumberFormatException e) { currentSeqMsg.setWPARAMasWindowDesc(paramValueStr); } } if (seqIterator.hasNext()) { // the first seq-var has a different number of elements than the // current one throw new NoSuchElementException(); } msgsGenerated = true; } else { // const value int paramValue = Integer.parseInt(getTermValue(null, termElement)); while (seqIterator.hasNext()) { seqIterator.next().setWPARAM(paramValue); } } return msgsGenerated; } /** *

* If a message sequence is already generated, i.e., msgsGenerated is true, * the seqIterator is used to iterate through these messages and return the * current one. If the message sequence is not yet generated, i.e., * msgsGenerated is false, the message sequence is generated on the fly * during each call of this message and the newly generated messages are * returned. *

* * @param generatedMessageSeq * message sequence * @param msgsGenerated * indicates if generatedMessageSeq is already generated or has * to be generated on the fly by this method * @param constMsgType * type of the message to be used for message generation * @param seqIterator * iterates through an already generated message sequence; must * not be {@code null}, if msgsGenerated is true * @return current message */ private WindowsMessage getCurrentSeqMsg( List generatedMessageSeq, boolean msgsGenerated, int constMsgType, Iterator seqIterator) { WindowsMessage currentSeqMsg = null; if (msgsGenerated) { currentSeqMsg = seqIterator.next(); } else { currentSeqMsg = new WindowsMessage(constMsgType); generatedMessageSeq.add(currentSeqMsg); } return currentSeqMsg; } // //////////////////////////// // General helper functions // // //////////////////////////// /** *

* Retrieves a message from the storage for, e.g., comparison or replay. * "this" is used to refer to the current message. *

* * @param currentMessage * current message during the parsing; passed to handle "this" * @param obj * object identifier in the storage * @return message retrieved from the storage * @throws InvalidParameterException * thrown in case of invalid uses of "this" or if no message * with the identifier obj is found in the storage */ private WindowsMessage getStoredMessageVariable( WindowsMessage currentMessage, String obj) throws InvalidParameterException { WindowsMessage varMessage = null; if (obj.equals("this")) { if (currentMessage == null) { throw new InvalidParameterException( "Failure obtaining term value for rule " + currentRuleName + ": \"this\" is not a valid name for generating runtime messages."); } varMessage = currentMessage; } else { Object tmp = messageStorage.get(obj); if (tmp instanceof WindowsMessage) { varMessage = (WindowsMessage) tmp; } else { throw new InvalidParameterException( "Failure obtaining term value for rule " + currentRuleName + ": No message \"" + obj + "\" stored."); } } return varMessage; } /** *

* Retrieves a stored message sequence from the storage. *

* * @param obj * object identifier in the storage * @return message sequence retrieved from the storage * @throws InvalidParameterException * thrown if no message sequences with the identifier obj is * found in the storage */ @SuppressWarnings("unchecked") private List getStoredSeqVariable(String obj) throws InvalidParameterException { List varMsgSeq = null; Object tmp = messageStorage.get(obj); if (tmp instanceof List) { varMsgSeq = (List) tmp; } else { throw new InvalidParameterException( "Failure obtaining term value for rule " + currentRuleName + ": No sequence \"" + obj + "\" store."); } return varMsgSeq; } /** *

* Handles term-nodes and returns the value of the described term. *

* * @param currentMessage * current message during the parsing; required to resolve * references to "this" in a term * @param termElement * {@link Element} representing the term node * @return value of the term or {@code null} of the term node could not be * evaluated */ private String getTermValue(WindowsMessage currentMessage, Element termElement) { String value = null; WindowsMessage varMessage = null; if (termElement.getName().equals("constValue")) { value = termElement.getAttributeValue("value"); } else if (termElement.getName().equals("paramValue")) { String objectName = termElement.getAttributeValue("obj"); varMessage = getStoredMessageVariable(currentMessage, objectName); if (varMessage != null) { String param = termElement.getAttributeValue("param"); value = varMessage.getParameter(param); } } else if (termElement.getName().equals("winInfoValue")) { String objectName = termElement.getAttributeValue("obj"); varMessage = getStoredMessageVariable(currentMessage, objectName); if (varMessage != null) { String paramString = termElement.getAttributeValue("winParam"); if (paramString.equals("class")) { value = varMessage.getWindowClass(); } else if (paramString.equals("resourceId")) { value = "" + varMessage.getWindowResourceId(); } else if (paramString.equals("hwnd")) { value = "" + varMessage.getHwnd(); } else if (paramString.equals("parentTarget")) { String target = varMessage.getXmlWindowDescription(); int index = target.lastIndexOf("<"); if (index == 0) { Console.traceln("Trying to adress parent of top-level window! Replay probably invalid!"); } value = target.substring(0, index); } else if (paramString.equals("parentClass")) { value = varMessage.getParentClass(); } } } else if (termElement.getName().equals("msgInfoValue")) { String objectName = termElement.getAttributeValue("obj"); varMessage = getStoredMessageVariable(currentMessage, objectName); if (varMessage != null) { String paramString = termElement.getAttributeValue("msgParam"); if (paramString.equals("type")) { value = "" + varMessage.getType(); } else if (paramString.equals("target")) { value = varMessage.getXmlWindowDescription(); } } } return value; } /** *

* Handles term-nodes contained by equalSeq nodes. *

* * @param termElement * {@link Element} representing the term-node * @return list of values of the term */ private List getTermValueSeq(Element termElement) { List values = new LinkedList(); if (termElement.getName().equals("seqValue")) { String obj = termElement.getAttributeValue("seqObj"); String param = termElement.getAttributeValue("param"); List seqVar = getStoredSeqVariable(obj); for (WindowsMessage msg : seqVar) { // msg.getParameter returns null, if parameter is not found, // therefore the List can contain null-values values.add(msg.getParameter(param)); } } return values; } /** *

* Handles LOWORD and HIWORD child nodes of LPARAM and WPARAM nodes. The * returned value is the LPARAM/WPARAM value based on the LOWORD and HIWORD. *

* * @param param * {@link Element} representing the LPARAM/WPARAM node * @return value of the LPARAM/WPARAM */ private long loHiWord(Element param) { Element loword = param.getChild("LOWORD", rulesNamespace); Element hiword = param.getChild("HIWORD", rulesNamespace); String lowordStr = getTermValue(null, (Element) loword.getChildren() .get(0)); String hiwordStr = getTermValue(null, (Element) hiword.getChildren() .get(0)); return MAKEPARAM(Short.parseShort(lowordStr), Short.parseShort(hiwordStr)); } /** *

* Takes to short integers and combines them into the high and low order * bits of an integer. *

* * @param loword * low word * @param hiword * high word * @return combined integer */ private static int MAKEPARAM(short loword, short hiword) { return loword | ((int) hiword) << Short.SIZE; } }