package de.ugoe.cs.quest.plugin.mfc; import java.io.IOException; import java.security.InvalidParameterException; import java.util.ArrayList; 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.eventcore.IEventType; import de.ugoe.cs.quest.plugin.mfc.EventGenerationRule.Term; import de.ugoe.cs.quest.plugin.mfc.eventcore.MFCEvent; import de.ugoe.cs.quest.plugin.mfc.eventcore.MFCEventTypeFactory; import de.ugoe.cs.quest.plugin.mfc.eventcore.ReplayWindowsMessage; import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessage; import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessageType; import de.ugoe.cs.quest.plugin.mfc.guimodel.MFCGUIElement; import de.ugoe.cs.quest.plugin.mfc.guimodel.WindowTree; 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 { /** *

* the list of all event generation rules available *

*/ private List generationRules; /** *

* 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 MFCEvent currentEvent; /** *

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

*/ private IEventType currentType; /** *

* *

*/ private MFCGUIElement currentTarget; /** *

* 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; /** *

* reference to the window tree created during parsing *

*/ private WindowTree windowTree; /** *

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

*/ public EventGenerator(WindowTree windowTree) { rulesFile = "data/rules.xml"; this.windowTree = windowTree; } /** *

* 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 */ public Event generateEvent(List sequence) { if (generationRules == null) { parseGenerationRules(); } /*System.out.println("generating event for "); for (WindowsMessage msg : sequence) { System.out.println(" " + msg + " " + msg.getParameter("scrollBarHandle") + " " + msg.getTargetXML()); }*/ boolean isMatch = false; for (int ruleIndex = 0; ruleIndex < generationRules.size() && !isMatch; ruleIndex++) { EventGenerationRule currentRule = generationRules.get(ruleIndex); currentRuleName = currentRule.getName(); //System.out.println("checking rule " + currentRuleName); messageStorage = new HashMap(); sequenceIterator = sequence.listIterator(); isMatch = evaluateMessageConditions(currentRule); if (isMatch) { currentType = MFCEventTypeFactory.getInstance().getEventType (currentRuleName, resolveParameters(currentRule.getEventParameters())); currentEvent = new MFCEvent(currentType, currentTarget, windowTree.getGUIModel()); for (EventGenerationRule.ReplayMessageSpec replayMessageSpec : currentRule.getReplayMessageSpecifications()) { if (replayMessageSpec.generateSingleMessage()) { try { generateReplayMessage(replayMessageSpec); } catch (InvalidParameterException e) { Console.printerrln(e.getMessage()); // TODO currentToken.invalidateReplay(); } } else { try { generateReplaySequence(replayMessageSpec); // TODO currentToken.invalidateReplay(); } catch (InvalidParameterException e) { Console.printerrln(e.getMessage()); // TODO currentToken.invalidateReplay(); } } } Console.traceln(currentEvent.getType().toString() + " matched"); } else { currentEvent = null; } } if (!isMatch) { Console.traceln("no match found for sequence: " + sequence.toString()); } /*if (currentEvent != null && currentEvent.getReplayables() != null) { System.out.println("replay messages are "); for (de.ugoe.cs.quest.eventcore.IReplayable msg : currentEvent.getReplayables()) { System.out.println(" " + msg + " " + msg.getReplay()); } } System.out.println();*/ return currentEvent; } // //////////////////////////////////////////////////////////// // Helper functions for matching of events, i.e., msg-nodes // // //////////////////////////////////////////////////////////// /** *

* TODO: comment *

* * @param currentRule */ private boolean evaluateMessageConditions(EventGenerationRule currentRule) { boolean isMatch = true; List messageConditions = currentRule.getMessageConditions(); int i = 0; while (isMatch && i < messageConditions.size()) { EventGenerationRule.MessageCondition messageCondition = messageConditions.get(i); if (messageCondition.matchMultiple()) { EventGenerationRule.MessageCondition nextMessageCondition = null; if (i + 1 < messageConditions.size()) { nextMessageCondition = messageConditions.get(i + 1); } try { isMatch = matchMultipleConditions(messageCondition, nextMessageCondition); } catch (InvalidParameterException e) { Console.printerrln(e.getMessage()); } } else { try { isMatch = matchSingleMessage(messageCondition); } catch (InvalidParameterException e) { Console.printerrln(e.getMessage()); } } i++; } return isMatch; } /** *

* 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(EventGenerationRule.MessageCondition condition) { boolean isMatch = false; WindowsMessage currentMessage = null; WindowsMessageType type = condition.getMessageType(); 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 = evaluateMessageCondition(currentMessage, condition); // in case the message is a match, eval storage children if (isMatch) { handleStorage(condition, currentMessage); currentTarget = currentMessage.getTarget(); // 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 matchMultipleConditions(EventGenerationRule.MessageCondition condition, EventGenerationRule.MessageCondition nextCondition) { boolean isMatch = false; boolean isCurrentMatch = false; boolean nextMatchFound = false; WindowsMessage currentMessage = null; WindowsMessage nextMessage = null; WindowsMessageType type = condition.getMessageType(); WindowsMessageType nextType = null; if (nextCondition != null) { nextType = nextCondition.getMessageType(); } while (!nextMatchFound && sequenceIterator.hasNext()) { currentMessage = sequenceIterator.next(); if (type == currentMessage.getType()) { isCurrentMatch = evaluateMessageCondition(currentMessage, condition); isMatch = isMatch || isCurrentMatch; if (isCurrentMatch) { handleStorage(condition, currentMessage); currentTarget = currentMessage.getTarget(); // TODO currentToken.setTargetShort(currentMessage.getParentNames()); } } if (nextCondition != 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 = evaluateMessageCondition(nextMessage, nextCondition); } } } 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 */ private boolean evaluateMessageCondition(WindowsMessage currentMessage, EventGenerationRule.MessageCondition condition) { boolean isMatch = true; for (int i = 0; isMatch && (i < condition.getAttributeConditions().size()); i++) { EventGenerationRule.AttributeCondition attrCond = condition.getAttributeConditions().get(i); // the size 2 of termElements is guaranteed by the XML schema Object value1 = getTermValue(currentMessage, attrCond.getLeftHandSide(), Object.class); Object value2 = getTermValue(currentMessage, attrCond.getRightHandSide(), Object.class); 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(EventGenerationRule.MessageCondition messageCondition, WindowsMessage currentMessage) { for (Term valueToStore : messageCondition.getMessagesToStore()) { if (valueToStore.getMessageId() != null) { ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(currentMessage); messageStorage.put(valueToStore.getMessageId(), replayMessage); resolveHwnd(replayMessage, valueToStore.getResolveHandles()); } else if (valueToStore.getSequenceId() != null) { Object tmp = messageStorage.get(valueToStore.getSequenceId()); ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(currentMessage); List storedSequence; if (tmp == null || tmp instanceof ReplayWindowsMessage) { storedSequence = new LinkedList(); storedSequence.add(replayMessage); messageStorage.put(valueToStore.getSequenceId(), storedSequence); } else if (tmp instanceof List) { storedSequence = (List) tmp; storedSequence.add(replayMessage); messageStorage.put(valueToStore.getSequenceId(), storedSequence); } resolveHwnd(replayMessage, valueToStore.getResolveHandles()); } } } /** *

* 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 list * child element of the store node that represents the resolve */ private void resolveHwnd(ReplayWindowsMessage currentMessage, List resolveHandles) { if (resolveHandles != null) { for (Term resolveElement : resolveHandles) { String param = resolveElement.getMessageParameterName(); String storeParam = resolveElement.getStoreParameterName(); long paramHwnd = (Long) currentMessage.getParameter(param); MFCGUIElement guiElement = windowTree.find(paramHwnd); if (guiElement != null) { currentMessage.addParameter(storeParam, "" + guiElement.toXML()); } } } } // ///////////////////////////////////////////////////// // 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 */ private void generateReplayMessage(EventGenerationRule.ReplayMessageSpec messageSpec) { ReplayWindowsMessage generatedMessage = null; if (messageSpec.getReplayObjectId() != null) { // replay stored message without change generatedMessage = getStoredMessageVariable(null, messageSpec.getReplayObjectId()); } else { // generate message according to the rule generatedMessage = new ReplayWindowsMessage (getTermValue(messageSpec.getType(), WindowsMessageType.class)); generatedMessage.setTargetXML(getTermValue(messageSpec.getTarget(), String.class)); if ((messageSpec.getLparamHiWord() != null) || (messageSpec.getLparamLoWord() != null)) { generatedMessage.setLPARAM (loHiWord(messageSpec.getLparamLoWord(), messageSpec.getLparamHiWord())); } else if (messageSpec.getLparam() != null) { try { generatedMessage.setLPARAM(getTermValue(messageSpec.getLparam(), Long.class)); } catch (IllegalArgumentException e) { generatedMessage.setLPARAMasWindowDesc (getTermValue(messageSpec.getLparam(), String.class)); } } if ((messageSpec.getWparamHiWord() != null) || (messageSpec.getWparamLoWord() != null)) { generatedMessage.setWPARAM (loHiWord(messageSpec.getWparamLoWord(), messageSpec.getWparamHiWord())); } else if (messageSpec.getWparam() != null) { try { generatedMessage.setWPARAM(getTermValue(messageSpec.getWparam(), Long.class)); } catch (IllegalArgumentException e) { generatedMessage.setWPARAMasWindowDesc (getTermValue(messageSpec.getWparam(), String.class)); } } } generatedMessage.setDelay(messageSpec.getDelay()); currentEvent.addReplayable(generatedMessage); } /** * Handles genMsgSeq-nodes and adds the replay to the {@link Event} that is generated.

* * @param genMsgElement * {@link Element} representing the genMsgSeq-node. */ private void generateReplaySequence(EventGenerationRule.ReplayMessageSpec messageSpec) { List generatedMessageSeq = new LinkedList(); if (messageSpec.getReplayObjectId() != null) { // replay stored sequence without changes generatedMessageSeq = getStoredSeqVariable(messageSpec.getReplayObjectId()); } else { List seqVar = getStoredSeqVariable(messageSpec.getReplayObjectId()); Term typeTerm = messageSpec.getType(); if (typeTerm.getSequenceId() != null) { seqVar = getStoredSeqVariable(typeTerm.getSequenceId()); for (WindowsMessage msg : seqVar) { ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(msg.getType()); replayMessage.setDelay(messageSpec.getDelay()); generatedMessageSeq.add(replayMessage); } } else { // constValue type WindowsMessageType constMsgType = getTermValue(typeTerm, WindowsMessageType.class); for (int i = 0; i < seqVar.size(); i++) { ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(constMsgType); replayMessage.setDelay(messageSpec.getDelay()); generatedMessageSeq.add(replayMessage); } } createSequenceTarget(generatedMessageSeq, messageSpec); createSequenceLParam(generatedMessageSeq, messageSpec); createSequenceWParam(generatedMessageSeq, messageSpec); } currentEvent.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 void createSequenceTarget(List generatedMessageSeq, EventGenerationRule.ReplayMessageSpec messageSpec) throws NoSuchElementException { Iterator seqIterator = generatedMessageSeq.iterator(); if (messageSpec.getTarget().getSequenceId() != null) { List seqVar = getStoredSeqVariable(messageSpec.getTarget().getSequenceId()); if (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) { seqIterator.next().setTarget(msg.getTarget()); } } 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. */ } } /** *

* 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 void createSequenceLParam(List generatedMessageSeq, EventGenerationRule.ReplayMessageSpec messageSpec) throws NoSuchElementException { Iterator seqIterator = generatedMessageSeq.iterator(); if (messageSpec.getLparam().getSequenceId() != null) { List seqVar = getStoredSeqVariable(messageSpec.getLparam().getSequenceId()); if (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 lengths."); } for (WindowsMessage msg : seqVar) { ReplayWindowsMessage currentSeqMsg = seqIterator.next(); Object paramValue = msg.getParameter(messageSpec.getLparam().getSequenceParameterName()); if (paramValue instanceof Long) { currentSeqMsg.setLPARAM((Long) paramValue); } else { currentSeqMsg.setLPARAMasWindowDesc((String) paramValue); } } } else { // const value int paramValue = getTermValue(messageSpec.getLparam(), int.class); while (seqIterator.hasNext()) { seqIterator.next().setLPARAM(paramValue); } } } /** *

* 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 void createSequenceWParam(List generatedMessageSeq, EventGenerationRule.ReplayMessageSpec messageSpec) throws NoSuchElementException { Iterator seqIterator = generatedMessageSeq.iterator(); if (messageSpec.getWparam().getSequenceId() != null) { List seqVar = getStoredSeqVariable(messageSpec.getWparam().getSequenceId()); if (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 lengths."); } for (WindowsMessage msg : seqVar) { ReplayWindowsMessage currentSeqMsg = seqIterator.next(); Object paramValue = msg.getParameter(messageSpec.getWparam().getSequenceParameterName()); if (paramValue instanceof Long) { currentSeqMsg.setWPARAM((Long) paramValue); } else { currentSeqMsg.setWPARAMasWindowDesc((String) paramValue); } } } else { // const value int paramValue = getTermValue(messageSpec.getWparam(), int.class); while (seqIterator.hasNext()) { seqIterator.next().setWPARAM(paramValue); } } } // //////////////////////////// // General helper functions // // //////////////////////////// /** *

* TODO: comment *

* * @param eventParameters * @return */ private Map resolveParameters(List eventParameters) { Map resultParameters = null; if ((eventParameters != null) && (eventParameters.size() > 0)) { resultParameters = new HashMap(); for (Term term : eventParameters) { if ("seqValue".equals(term.getName())) { List values = getTermValueAsList(term, String.class); resultParameters.put (term.getSequenceParameterName(), (String) values.get(values.size() - 1)); } else { resultParameters.put (term.getMessageParameterName(), getTermValue(term, String.class)); } } } return resultParameters; } /** *

* 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 ReplayWindowsMessage getStoredMessageVariable(WindowsMessage currentMessage, String obj) throws InvalidParameterException { ReplayWindowsMessage 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 = new ReplayWindowsMessage(currentMessage); } else { Object tmp = messageStorage.get(obj); if (tmp instanceof ReplayWindowsMessage) { varMessage = (ReplayWindowsMessage) 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 IllegalArgumentException * thrown if no message sequences with the identifier obj is found in the storage */ @SuppressWarnings("unchecked") private List getStoredSeqVariable(String obj) throws IllegalArgumentException { List varMsgSeq = null; Object tmp = messageStorage.get(obj); if (tmp instanceof List) { varMsgSeq = (List) tmp; } else { throw new IllegalArgumentException("Failure obtaining term value for rule " + currentRuleName + ": No sequence \"" + obj + "\" store."); } return varMsgSeq; } /** *

* convenience method for {@link #getTermValue(WindowsMessage, Term)} with current message is * null. *

* * @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 T getTermValue(EventGenerationRule.Term term, Class expectedType) { return getTermValue(null, term, expectedType); } /** *

* 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 T getTermValue(WindowsMessage currentMessage, EventGenerationRule.Term term, Class expectedType) { T value = null; if ("constValue".equals(term.getName())) { value = getValueAsType(term.getValue(), expectedType); } else if ("paramValue".equals(term.getName())) { String objectName = term.getMessageId(); WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName); if (varMessage != null) { String param = term.getMessageParameterName(); value = getValueAsType(varMessage.getParameter(param), expectedType); } } else if ("winInfoValue".equals(term.getName())) { String objectName = term.getMessageId(); WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName); if (varMessage != null) { String paramString = term.getWindowParameterName(); if (paramString.equals("class")) { value = getValueAsType (((MFCGUIElement) varMessage.getTarget()).getType(), expectedType); } else if (paramString.equals("resourceId")) { value = getValueAsType (((MFCGUIElement) varMessage.getTarget()).getResourceId(), expectedType); } else if (paramString.equals("hwnd")) { value = getValueAsType (((MFCGUIElement) varMessage.getTarget()).getId(), expectedType); } else if (paramString.equals("parentTarget")) { String target = varMessage.getTargetXML(); int index = target.lastIndexOf("<"); if (index == 0) { Console.traceln("Trying to adress parent of top-level window! Replay " + "probably invalid!"); } value = getValueAsType(target.substring(0, index), expectedType); } else if (paramString.equals("parentClass")) { value = getValueAsType (((MFCGUIElement) varMessage.getTarget()) .getParent().getSpecification().getType(), expectedType); } } } else if ("msgInfoValue".equals(term.getName())) { String objectName = term.getMessageId(); WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName); if (varMessage != null) { String paramString = term.getMessageInfoName(); if (paramString.equals("type")) { value = getValueAsType(varMessage.getType(), expectedType); } else if (paramString.equals("target")) { value = getValueAsType(varMessage.getTargetXML(), expectedType); } } } else if ("msgInfoValue".equals(term.getName())) { String objectName = term.getMessageId(); WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName); if (varMessage != null) { String paramString = term.getMessageInfoName(); if (paramString.equals("type")) { value = getValueAsType(varMessage.getType(), expectedType); } else if (paramString.equals("target")) { value = getValueAsType(varMessage.getTargetXML(), expectedType); } } } return value; } /** *

* convenience method for {@link #getTermValueAsList(WindowsMessage, Term)} with current * message is null. *

* * @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 List getTermValueAsList(EventGenerationRule.Term term, Class expectedType) { return getTermValueAsList(null, term, expectedType); } /** *

* 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 List getTermValueAsList(WindowsMessage currentMessage, EventGenerationRule.Term term, Class expectedElementType) { List values = new ArrayList(); if ("seqValue".equals(term.getName())) { List seqVar = getStoredSeqVariable(term.getSequenceId()); Object value; for (ReplayWindowsMessage msg : seqVar) { // msg.getParameter returns null, if parameter is not found, // therefore the List can contain null-values value = msg.getParameter(term.getSequenceParameterName()); values.add(getValueAsType(value, expectedElementType)); } } else { values.add(getTermValue(currentMessage, term, expectedElementType)); } return values; } /** *

* TODO: comment *

* * @param value * @param expectedType * @return */ @SuppressWarnings("unchecked") private T getValueAsType(Object value, Class expectedType) { if (expectedType.isInstance(value)) { return (T) value; } else if (value instanceof String) { try { if (WindowsMessageType.class.equals(expectedType)) { return (T) WindowsMessageType.parseMessageType((String) value); } else if (Short.class.equals(expectedType)) { return (T) (Short) Short.parseShort((String) value); } } catch (Exception e) { // in this case, the value can not be transformed to the expected value. So ignore // the exception and fall through to the exception thrown anyway } } else if (value instanceof Long) { try { if (Short.class.equals(expectedType)) { return (T) (Short) ((Long) value).shortValue(); } else if (String.class.equals(expectedType)) { return (T) ((Long) value).toString(); } } catch (Exception e) { // in this case, the value can not be transformed to the expected value. So ignore // the exception and fall through to the exception thrown anyway } } throw new IllegalArgumentException("the term value is not of the expected type " + expectedType + " but a " + (value != null ? value.getClass() : "null")); } /** *

* 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(EventGenerationRule.Term lword, EventGenerationRule.Term hword) { return MAKEPARAM(getTermValue(lword, Short.class), getTermValue(hword, Short.class)); } /** *

* 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; } /** *

* TODO: comment *

* */ @SuppressWarnings("unchecked") private void parseGenerationRules() { SAXBuilder builder = new SAXBuilder(); Document doc = null; try { doc = builder.build(rulesFile); 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(); } Element rulesRoot = doc.getRootElement(); List ruleElements = rulesRoot.getChildren("rule", rulesNamespace); generationRules = new ArrayList(); for (Element ruleElement : ruleElements) { generationRules.add(new EventGenerationRule(ruleElement, rulesNamespace)); } } }