source: trunk/quest-plugin-mfc/src/main/java/de/ugoe/cs/quest/plugin/mfc/EventGenerator.java @ 837

Last change on this file since 837 was 837, checked in by sherbold, 12 years ago
  • code documentation and clean-up
File size: 44.2 KB
Line 
1package de.ugoe.cs.quest.plugin.mfc;
2
3import java.io.IOException;
4import java.util.ArrayList;
5import java.util.HashMap;
6import java.util.Iterator;
7import java.util.LinkedList;
8import java.util.List;
9import java.util.ListIterator;
10import java.util.Map;
11import java.util.NoSuchElementException;
12import java.util.logging.Level;
13
14import org.jdom.Document;
15import org.jdom.Element;
16import org.jdom.JDOMException;
17import org.jdom.Namespace;
18import org.jdom.input.SAXBuilder;
19
20import de.ugoe.cs.quest.eventcore.Event;
21import de.ugoe.cs.quest.eventcore.IEventType;
22import de.ugoe.cs.quest.plugin.mfc.EventGenerationRule.Term;
23import de.ugoe.cs.quest.plugin.mfc.eventcore.MFCEventTypeFactory;
24import de.ugoe.cs.quest.plugin.mfc.eventcore.ReplayWindowsMessage;
25import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessage;
26import de.ugoe.cs.quest.plugin.mfc.eventcore.WindowsMessageType;
27import de.ugoe.cs.quest.plugin.mfc.guimodel.MFCGUIElement;
28import de.ugoe.cs.quest.plugin.mfc.guimodel.WindowTree;
29import de.ugoe.cs.util.console.Console;
30
31/**
32 * <p>
33 * Translates sequences of windows messages into {@link WindowsEvent}s that can be used by the
34 * QUEST core libraries.
35 * </p>
36 *
37 * @version 1.0
38 * @author Steffen Herbold, Patrick Harms
39 */
40public class EventGenerator {
41
42    /**
43     * <p>
44     * the list of all event generation rules available
45     * </p>
46     */
47    private List<EventGenerationRule> generationRules;
48   
49    /**
50     * <p>
51     * Name and path of the XML files containing the rules.
52     * </p>
53     */
54    private String rulesFile;
55
56    /**
57     * <p>
58     * Iterator used for the current sequence.
59     * </p>
60     */
61    private ListIterator<WindowsMessage> sequenceIterator;
62
63    /**
64     * <p>
65     * Token that is currently being generated.
66     * </p>
67     */
68    private Event currentEvent;
69
70    /**
71     * <p>
72     * Event type of the current token. Stored as a member to be able to update it during the
73     * parsing of the idinfo tag.
74     * </p>
75     */
76    private IEventType currentType;
77
78    /**
79     * <p>
80     *
81     * </p>
82     */
83    private MFCGUIElement currentTarget;
84
85    /**
86     * <p>
87     * Reference to the ul:rules namespace.
88     * </p>
89     */
90    private static Namespace rulesNamespace;
91
92    /**
93     * <p>
94     * The name of the rule that is currently being evaluated.
95     * </p>
96     */
97    private String currentRuleName;
98
99    /**
100     * <p>
101     * Internal message storage. Used to implement the <code>{@literal <store>}</code> and
102     * <code>{@literal <storeSeq>}</code> tags.
103     * </p>
104     */
105    private Map<String, Object> messageStorage;
106
107    /**
108     * <p>
109     * reference to the window tree created during parsing
110     * </p>
111     */
112    private WindowTree windowTree;
113
114    /**
115     * <p>
116     * Creates a new EventGenerator. Sets "data/rules.xml" as default file for the rules.
117     * </p>
118     */
119    public EventGenerator(WindowTree windowTree) {
120        rulesFile = "data/rules.xml";
121        this.windowTree = windowTree;
122    }
123
124    /**
125     * <p>
126     * Tries to match the rules to the given sequence to generate an {@link WindowsEvent}.
127     * </p>
128     * <p>
129     * The rules are matched the order, in which they are defined in the XML file. Therefore, the
130     * order of the rules in the file defines priorities, when multiple rules could be matched to
131     * the same sequence.
132     * </p>
133     *
134     * @param sequence
135     *            sequence of message for which an event will be generated
136     * @return event that matches the messages; null, if no rule can be matched
137     */
138    public Event generateEvent(List<WindowsMessage> sequence) {
139        if (generationRules == null) {
140            parseGenerationRules();
141        }
142       
143       
144        /*System.out.println("generating event for ");
145        for (WindowsMessage msg : sequence) {
146            System.out.println("    " + msg + "  " + msg.getParameter("scrollBarHandle") + "  " + msg.getTargetXML());
147        }*/
148
149        boolean isMatch = false;
150
151        for (int ruleIndex = 0; ruleIndex < generationRules.size() && !isMatch; ruleIndex++) {
152            EventGenerationRule currentRule = generationRules.get(ruleIndex);
153            currentRuleName = currentRule.getName();
154           
155            //System.out.println("checking rule " + currentRuleName);
156           
157            messageStorage = new HashMap<String, Object>();
158            sequenceIterator = sequence.listIterator();
159           
160            isMatch = evaluateMessageConditions(currentRule);
161
162            if (isMatch) {
163                currentType = MFCEventTypeFactory.getInstance().getEventType
164                    (currentRuleName, resolveParameters(currentRule.getEventParameters()));
165               
166                currentEvent = new Event(currentType, currentTarget);
167               
168                for (EventGenerationRule.ReplayMessageSpec replayMessageSpec :
169                      currentRule.getReplayMessageSpecifications())
170                {
171                    if (replayMessageSpec.generateSingleMessage()) {
172                        try {
173                            generateReplayMessage(replayMessageSpec);
174                        }
175                        catch (IllegalArgumentException e) {
176                            Console.printerrln(e.getMessage());
177                            // TODO currentToken.invalidateReplay();
178                        }
179                    }
180                    else {
181                        try {
182                            generateReplaySequence(replayMessageSpec);
183                            // TODO currentToken.invalidateReplay();
184                        }
185                        catch (IllegalArgumentException e) {
186                            Console.printerrln(e.getMessage());
187                            // TODO currentToken.invalidateReplay();
188                        }
189                    }
190                }
191
192                Console.traceln(Level.FINE, currentEvent.getType().toString() + " matched");
193            }
194            else {
195                currentEvent = null;
196            }
197        }
198        if (!isMatch) {
199            Console.traceln(Level.WARNING, "no match found for sequence: " + sequence.toString());
200        }
201       
202       
203        /*if (currentEvent != null && currentEvent.getReplayables() != null) {
204            System.out.println("replay messages are ");
205            for (de.ugoe.cs.quest.eventcore.IReplayable msg : currentEvent.getReplayables()) {
206                System.out.println("    " + msg + "  " + msg.getReplay());
207            }
208        }
209
210        System.out.println();*/
211
212
213        return currentEvent;
214    }
215
216    // ////////////////////////////////////////////////////////////
217    // Helper functions for matching of events, i.e., msg-nodes //
218    // ////////////////////////////////////////////////////////////
219
220    /**
221     * <p>
222     * Evaluates whether the current message sequence matches a given rule.
223     * </p>
224     *
225     * @param currentRule rule that is matched
226     * @return true if the message sequence matches the rule; false otherwise
227     */
228    private boolean evaluateMessageConditions(EventGenerationRule currentRule) {
229        boolean isMatch = true;
230        List<EventGenerationRule.MessageCondition> messageConditions =
231            currentRule.getMessageConditions();
232
233        int i = 0;
234        while (isMatch && i < messageConditions.size()) {
235            EventGenerationRule.MessageCondition messageCondition = messageConditions.get(i);
236            if (messageCondition.matchMultiple()) {
237                EventGenerationRule.MessageCondition nextMessageCondition = null;
238                if (i + 1 < messageConditions.size()) {
239                    nextMessageCondition = messageConditions.get(i + 1);
240                }
241                try {
242                    isMatch = matchMultipleConditions(messageCondition, nextMessageCondition);
243                }
244                catch (IllegalArgumentException e) {
245                    Console.printerrln(e.getMessage());
246                }
247            }
248            else {
249                try {
250                    isMatch = matchSingleMessage(messageCondition);
251                }
252                catch (IllegalArgumentException e) {
253                    Console.printerrln(e.getMessage());
254                }
255            }
256            i++;
257        }
258       
259        return isMatch;
260    }
261
262    /**
263     * <p>
264     * Handles msg-nodes where multiple is not true, i.e., not a sequences.
265     * </p>
266     *
267     * @param messageElement
268     *            {@link Element} representing the msg-node
269     * @return true, if a match is found; false otherwise
270     */
271    private boolean matchSingleMessage(EventGenerationRule.MessageCondition condition) {
272        boolean isMatch = false;
273        WindowsMessage currentMessage = null;
274
275        WindowsMessageType type = condition.getMessageType();
276
277        while (!isMatch && sequenceIterator.hasNext()) {
278            /*
279             * traverses the messages from the current position forward till a message with the
280             * correct type is found
281             */
282            currentMessage = sequenceIterator.next();
283            if (type == currentMessage.getType()) {
284                // message with the correct type found
285                // eval child nodes for further matching/storing
286                isMatch = evaluateMessageCondition(currentMessage, condition);
287
288                // in case the message is a match, eval storage children
289                if (isMatch) {
290                    handleStorage(condition, currentMessage);
291                    currentTarget = currentMessage.getTarget();
292                    // TODO currentToken.setTargetShort(currentMessage.getParentNames());
293                }
294            }
295        }
296
297        return isMatch;
298    }
299
300    /**
301     * <p>
302     * Handles msg-nodes where multiple is true, i.e., sequences. Requires knowledge about the next
303     * msg-node to determine the end of the sequence.
304     * </p>
305     *
306     * @param messageElement
307     *            {@link Element} representing the msg-node
308     * @param nextMessageElement
309     *            {@link Element} representing the next msg-node; {@code null} if the current node
310     *            is the last one
311     * @return true, if a sequence is matched; false otherwise
312     */
313    private boolean matchMultipleConditions(EventGenerationRule.MessageCondition condition,
314                                            EventGenerationRule.MessageCondition nextCondition)
315    {
316        boolean isMatch = false;
317        boolean isCurrentMatch = false;
318        boolean nextMatchFound = false;
319        WindowsMessage currentMessage = null;
320        WindowsMessage nextMessage = null;
321
322        WindowsMessageType type = condition.getMessageType();
323
324        WindowsMessageType nextType = null;
325        if (nextCondition != null) {
326            nextType = nextCondition.getMessageType();
327        }
328
329        while (!nextMatchFound && sequenceIterator.hasNext()) {
330            currentMessage = sequenceIterator.next();
331            if (type == currentMessage.getType()) {
332                isCurrentMatch = evaluateMessageCondition(currentMessage, condition);
333                isMatch = isMatch || isCurrentMatch;
334
335                if (isCurrentMatch) {
336                    handleStorage(condition, currentMessage);
337                    currentTarget = currentMessage.getTarget();
338                    // TODO currentToken.setTargetShort(currentMessage.getParentNames());
339                }
340            }
341            if (nextCondition != null && isMatch) {
342                // peek next message to check if the sequence ends and the next
343                // match is found
344                if (!sequenceIterator.hasNext()) {
345                    return false; // sequence is over, but not all messages are
346                                  // found
347                }
348                nextMessage = sequenceIterator.next();
349                sequenceIterator.previous();
350
351                if (nextType == nextMessage.getType()) {
352                    nextMatchFound = evaluateMessageCondition(nextMessage, nextCondition);
353                }
354
355            }
356        }
357
358        return isMatch;
359    }
360
361    /**
362     * <p>
363     * Handles equals-nodes.
364     * </p>
365     *
366     * @param currentMessage
367     *            {@link Element} representing the msg-node the equals-node belongs to
368     * @param messageElement
369     *            {@link Element} representing the equals-node to be evaluated
370     * @return true, if constraint is fulfilled; false otherwise
371     */
372    private boolean evaluateMessageCondition(WindowsMessage                       currentMessage,
373                                             EventGenerationRule.MessageCondition condition)
374    {
375        boolean isMatch = true;
376        for (int i = 0; isMatch && (i < condition.getAttributeConditions().size()); i++)
377        {
378            EventGenerationRule.AttributeCondition attrCond =
379                condition.getAttributeConditions().get(i);
380           
381            // the size 2 of termElements is guaranteed by the XML schema
382            Object value1 = getTermValue(currentMessage, attrCond.getLeftHandSide(), Object.class);
383            Object value2 = getTermValue(currentMessage, attrCond.getRightHandSide(), Object.class);
384            if (value1 == null || value2 == null) {
385                isMatch = false;
386            }
387            else {
388                isMatch = isMatch && value1.equals(value2);
389            }
390        }
391//        for (Element childElement : (List<Element>) messageElement.getChildren("equalsSeq",
392//                                                                               rulesNamespace))
393//        {
394//            List<Element> termElements = childElement.getChildren();
395//            List<String> values1 = getTermValueSeq(termElements.get(0));
396//            List<String> values2 = getTermValueSeq(termElements.get(0));
397//            if (values1 == null || values2 == null) {
398//                isMatch = false;
399//            }
400//            else {
401//                isMatch = isMatch && values1.equals(values2);
402//            }
403//        }
404        return isMatch;
405    }
406
407    /**
408     * <p>
409     * Handles store-nodes and storeSeq-nodes.
410     * </p>
411     *
412     * @param messageElement
413     *            {@link Element} representing the msg-node that is currently being evaluated
414     * @param currentMessage
415     *            current message in the message sequence that is matched; this is the message that
416     *            is stored
417     */
418    @SuppressWarnings("unchecked")
419    private void handleStorage(EventGenerationRule.MessageCondition messageCondition,
420                               WindowsMessage                       currentMessage)
421    {
422        for (Term valueToStore : messageCondition.getMessagesToStore())
423        {
424            if (valueToStore.getMessageId() != null) {
425              ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(currentMessage);
426              messageStorage.put(valueToStore.getMessageId(), replayMessage);
427              resolveHwnd(replayMessage, valueToStore.getResolveHandles());
428            }
429            else if (valueToStore.getSequenceId() != null) {
430                Object tmp = messageStorage.get(valueToStore.getSequenceId());
431                ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(currentMessage);
432                List<ReplayWindowsMessage> storedSequence;
433                if (tmp == null || tmp instanceof ReplayWindowsMessage) {
434                    storedSequence = new LinkedList<ReplayWindowsMessage>();
435                    storedSequence.add(replayMessage);
436                    messageStorage.put(valueToStore.getSequenceId(), storedSequence);
437                }
438                else if (tmp instanceof List<?>) {
439                    storedSequence = (List<ReplayWindowsMessage>) tmp;
440                    storedSequence.add(replayMessage);
441                    messageStorage.put(valueToStore.getSequenceId(), storedSequence);
442                }
443                resolveHwnd(replayMessage, valueToStore.getResolveHandles());
444            }
445        }
446    }
447
448    /**
449     * <p>
450     * Resolves a parameter that contains a HWND of a message to the target string of the HWND and
451     * stores it.
452     * </p>
453     *
454     * @param currentMessage
455     *            message whose HWND is resolved
456     * @param list
457     *            child element of the store node that represents the resolve
458     */
459    private void resolveHwnd(ReplayWindowsMessage currentMessage, List<Term> resolveHandles) {
460        if (resolveHandles != null) {
461            for (Term resolveElement : resolveHandles) {
462                String param = resolveElement.getMessageParameterName();
463                String storeParam = resolveElement.getStoreParameterName();
464                long paramHwnd = (Long) currentMessage.getParameter(param);
465                MFCGUIElement guiElement = windowTree.find(paramHwnd);
466                if (guiElement != null) {
467                    currentMessage.addParameter(storeParam, "" + guiElement.toXML());
468                }
469            }
470        }
471    }
472
473    // /////////////////////////////////////////////////////
474    // Helper functions for generating the replay, i.e.,
475    // parsing of genMsg und genMsgSeq-nodes
476    // /////////////////////////////////////////////////////
477
478    /**
479     * <p>
480     * Handles genMsg-nodes and adds the replay to the {@link Event} that is generated.
481     * </p>
482     *
483     * @param genMsgElement
484     *            {@link Element} representing the genMsg-node
485     */
486    private void generateReplayMessage(EventGenerationRule.ReplayMessageSpec messageSpec) {
487        ReplayWindowsMessage generatedMessage = null;
488        if (messageSpec.getReplayObjectId() != null) { // replay stored message without change
489            generatedMessage = getStoredMessageVariable(null, messageSpec.getReplayObjectId());
490        }
491        else { // generate message according to the rule
492            generatedMessage = new ReplayWindowsMessage
493                (getTermValue(messageSpec.getType(), WindowsMessageType.class));
494            generatedMessage.setTargetXML(getTermValue(messageSpec.getTarget(), String.class));
495
496            if ((messageSpec.getLparamHiWord() != null) ||
497                (messageSpec.getLparamLoWord() != null))
498            {
499                generatedMessage.setLPARAM
500                    (loHiWord(messageSpec.getLparamLoWord(), messageSpec.getLparamHiWord()));
501            }
502            else if (messageSpec.getLparam() != null) {
503                try {
504                    generatedMessage.setLPARAM(getTermValue(messageSpec.getLparam(), Long.class));
505                }
506                catch (IllegalArgumentException e) {
507                    generatedMessage.setLPARAMasWindowDesc
508                        (getTermValue(messageSpec.getLparam(), String.class));
509                }
510            }
511
512            if ((messageSpec.getWparamHiWord() != null) ||
513                (messageSpec.getWparamLoWord() != null))
514            {
515                generatedMessage.setWPARAM
516                    (loHiWord(messageSpec.getWparamLoWord(), messageSpec.getWparamHiWord()));
517            }
518            else if (messageSpec.getWparam() != null) {
519                try {
520                    generatedMessage.setWPARAM(getTermValue(messageSpec.getWparam(), Long.class));
521                }
522                catch (IllegalArgumentException e) {
523                    generatedMessage.setWPARAMasWindowDesc
524                        (getTermValue(messageSpec.getWparam(), String.class));
525                }
526            }
527           
528           
529        }
530
531        generatedMessage.setDelay(messageSpec.getDelay());
532
533        currentEvent.addReplayable(generatedMessage);
534    }
535
536    /**
537     * Handles genMsgSeq-nodes and adds the replay to the {@link Event} that is generated.</p>
538     *
539     * @param genMsgElement
540     *            {@link Element} representing the genMsgSeq-node.
541     */
542    private void generateReplaySequence(EventGenerationRule.ReplayMessageSpec messageSpec)
543    {
544        List<ReplayWindowsMessage> generatedMessageSeq = new LinkedList<ReplayWindowsMessage>();
545        if (messageSpec.getReplayObjectId() != null) { // replay stored sequence without changes
546            generatedMessageSeq = getStoredSeqVariable(messageSpec.getReplayObjectId());
547        }
548        else {
549            List<ReplayWindowsMessage> seqVar =
550                getStoredSeqVariable(messageSpec.getReplayObjectId());
551           
552            Term typeTerm = messageSpec.getType();
553            if (typeTerm.getSequenceId() != null) {
554                seqVar = getStoredSeqVariable(typeTerm.getSequenceId());
555                for (WindowsMessage msg : seqVar) {
556                    ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(msg.getType());
557                    replayMessage.setDelay(messageSpec.getDelay());
558                    generatedMessageSeq.add(replayMessage);
559                }
560            }
561            else { // constValue type
562                WindowsMessageType constMsgType = getTermValue(typeTerm, WindowsMessageType.class);
563                for (int i = 0; i < seqVar.size(); i++) {
564                    ReplayWindowsMessage replayMessage = new ReplayWindowsMessage(constMsgType);
565                    replayMessage.setDelay(messageSpec.getDelay());
566                    generatedMessageSeq.add(replayMessage);
567                }
568            }
569
570            createSequenceTarget(generatedMessageSeq, messageSpec);
571            createSequenceLParam(generatedMessageSeq, messageSpec);
572            createSequenceWParam(generatedMessageSeq, messageSpec);
573        }
574       
575        currentEvent.addReplayableSequence(generatedMessageSeq);
576    }
577
578    /**
579     * <p>
580     * Creates the targets for replay sequences generated with genMsgSeq-nodes.
581     * </p>
582     *
583     * @param generatedMessageSeq
584     *            list of the messages that is being generated
585     * @param msgsGenerated
586     *            boolean stating if the list of messages is already generated or if the generation
587     *            has to be handles by this method
588     * @param constMsgType
589     *            a constant message type that is used for message generation, in case the list of
590     *            message is generated by this method
591     * @param termElement
592     *            {@link Element} representing the term-node describing the target
593     * @return true, if the list of message is generated after calling this method; false otherwise
594     * @throws NoSuchElementException
595     *             thrown if the seqVar referred to in the termElement contains a different number
596     *             of messages than is contained in messageSeq
597     */
598    private void createSequenceTarget(List<ReplayWindowsMessage>            generatedMessageSeq,
599                                      EventGenerationRule.ReplayMessageSpec messageSpec)
600        throws NoSuchElementException
601    {
602        Iterator<ReplayWindowsMessage> seqIterator = generatedMessageSeq.iterator();
603        if (messageSpec.getTarget().getSequenceId() != null) {
604            List<ReplayWindowsMessage> seqVar =
605                getStoredSeqVariable(messageSpec.getTarget().getSequenceId());
606
607            if (seqVar.size() != generatedMessageSeq.size()) {
608                throw new IllegalArgumentException
609                    ("Failure generating replay sequence for rule " + currentRuleName +
610                     ": One or more of the sequence variables used to generate a sequence have " +
611                     "different lenghts.");
612            }
613           
614            for (WindowsMessage msg : seqVar) {
615                seqIterator.next().setTarget(msg.getTarget());
616            }
617        }
618        else { // const value
619            throw new AssertionError("target must be a sequence variable!");
620            /*
621             * If target would not be a variable, the message-elements could not yet be created and
622             * the whole sequence might be broken. If this is to be changed, createSequenceLParam
623             * and createSequenceWParam need to be addepted, too.
624             */
625        }
626    }
627
628    /**
629     * <p>
630     * Creates the LPARAMs for replay sequences generated with genMsgSeq-nodes.
631     * </p>
632     *
633     * @param generatedMessageSeq
634     *            list of the messages that is being generated
635     * @param msgsGenerated
636     *            boolean stating if the list of messages is already generated or if the generation
637     *            has to be handles by this method
638     * @param constMsgType
639     *            a constant message type that is used for message generation, in case the list of
640     *            message is generated by this method
641     * @param termElement
642     *            {@link Element} representing the term-node describing the LPARAM
643     * @return true, if the list of message is generated after calling this method; false otherwise
644     * @throws NoSuchElementException
645     *             thrown if the seqVar referred to in the termElement contains a different number
646     *             of messages than is contained in messageSeq
647     */
648    private void createSequenceLParam(List<ReplayWindowsMessage>            generatedMessageSeq,
649                                      EventGenerationRule.ReplayMessageSpec messageSpec)
650        throws NoSuchElementException
651    {
652        Iterator<ReplayWindowsMessage> seqIterator = generatedMessageSeq.iterator();
653        if (messageSpec.getLparam().getSequenceId() != null) {
654            List<ReplayWindowsMessage> seqVar =
655                getStoredSeqVariable(messageSpec.getLparam().getSequenceId());
656           
657            if (seqVar.size() != generatedMessageSeq.size()) {
658                throw new IllegalArgumentException
659                    ("Failure generating replay sequence for rule " + currentRuleName +
660                     ": One or more of the sequence variables used to generate a sequence have " +
661                     "different lengths.");
662            }
663            for (WindowsMessage msg : seqVar) {
664                ReplayWindowsMessage currentSeqMsg = seqIterator.next();
665                Object paramValue =
666                    msg.getParameter(messageSpec.getLparam().getSequenceParameterName());
667                if (paramValue instanceof Long) {
668                    currentSeqMsg.setLPARAM((Long) paramValue);
669                }
670                else {
671                    currentSeqMsg.setLPARAMasWindowDesc((String) paramValue);
672                }
673            }
674        }
675        else { // const value
676            int paramValue = getTermValue(messageSpec.getLparam(), int.class);
677            while (seqIterator.hasNext()) {
678                seqIterator.next().setLPARAM(paramValue);
679            }
680        }
681
682    }
683
684    /**
685     * <p>
686     * Creates the WPARAMs for replay sequences generated with genMsgSeq-nodes.
687     * </p>
688     *
689     * @param generatedMessageSeq
690     *            list of the messages that is being generated
691     * @param msgsGenerated
692     *            boolean stating if the list of messages is already generated or if the generation
693     *            has to be handles by this method
694     * @param constMsgType
695     *            a constant message type that is used for message generation, in case the list of
696     *            message is generated by this method
697     * @param termElement
698     *            {@link Element} representing the term-node describing the WPARAM
699     * @return true, if the list of message is generated after calling this method; false otherwise
700     * @throws NoSuchElementException
701     *             thrown if the seqVar referred to in the termElement contains a different number
702     *             of messages than is contained in messageSeq
703     */
704    private void createSequenceWParam(List<ReplayWindowsMessage>            generatedMessageSeq,
705                                      EventGenerationRule.ReplayMessageSpec messageSpec)
706        throws NoSuchElementException
707    {
708        Iterator<ReplayWindowsMessage> seqIterator = generatedMessageSeq.iterator();
709        if (messageSpec.getWparam().getSequenceId() != null) {
710            List<ReplayWindowsMessage> seqVar =
711                getStoredSeqVariable(messageSpec.getWparam().getSequenceId());
712           
713            if (seqVar.size() != generatedMessageSeq.size()) {
714                throw new IllegalArgumentException
715                    ("Failure generating replay sequence for rule " + currentRuleName +
716                     ": One or more of the sequence variables used to generate a sequence have " +
717                     "different lengths.");
718            }
719            for (WindowsMessage msg : seqVar) {
720                ReplayWindowsMessage currentSeqMsg = seqIterator.next();
721                Object paramValue =
722                    msg.getParameter(messageSpec.getWparam().getSequenceParameterName());
723                if (paramValue instanceof Long) {
724                    currentSeqMsg.setWPARAM((Long) paramValue);
725                }
726                else {
727                    currentSeqMsg.setWPARAMasWindowDesc((String) paramValue);
728                }
729            }
730        }
731        else { // const value
732            long paramValue = getTermValue(messageSpec.getWparam(), Long.class);
733            while (seqIterator.hasNext()) {
734                seqIterator.next().setWPARAM(paramValue);
735            }
736        }
737    }
738
739    // ////////////////////////////
740    // General helper functions //
741    // ////////////////////////////
742
743    /**
744     * <p>
745     * Resolves the parameters described by {@link Term}s.
746     * </p>
747     *
748     * @param eventParameters terms whose parameters are resolved
749     * @return resolved parameters
750     */
751    private Map<String, String> resolveParameters(List<Term> eventParameters) {
752        Map<String, String> resultParameters = null;
753       
754        if ((eventParameters != null) && (eventParameters.size() > 0)) {
755            resultParameters = new HashMap<String, String>();
756           
757            for (Term term : eventParameters) {
758                if ("seqValue".equals(term.getName())) {
759                    List<String> values = getTermValueAsList(term, String.class);
760                                       
761                    resultParameters.put
762                        (term.getSequenceParameterName(), (String) values.get(values.size() - 1));
763                }
764                else {
765                    resultParameters.put
766                        (term.getMessageParameterName(), getTermValue(term, String.class));
767                }
768            }
769        }
770       
771        return resultParameters;
772    }
773
774    /**
775     * <p>
776     * Retrieves a message from the storage for, e.g., comparison or replay. "this" is used to refer
777     * to the current message.
778     * </p>
779     *
780     * @param currentMessage
781     *            current message during the parsing; passed to handle "this"
782     * @param obj
783     *            object identifier in the storage
784     * @return message retrieved from the storage
785     * @throws IllegalArgumentException
786     *             thrown in case of invalid uses of "this" or if no message with the identifier obj
787     *             is found in the storage
788     */
789    private ReplayWindowsMessage getStoredMessageVariable(WindowsMessage currentMessage, String obj)
790        throws IllegalArgumentException
791    {
792        ReplayWindowsMessage varMessage = null;
793        if (obj.equals("this")) {
794            if (currentMessage == null) {
795                throw new IllegalArgumentException("Failure obtaining term value for rule " +
796                    currentRuleName +
797                    ": \"this\" is not a valid name for generating runtime messages.");
798            }
799            varMessage = new ReplayWindowsMessage(currentMessage);
800        }
801        else {
802            Object tmp = messageStorage.get(obj);
803            if (tmp instanceof ReplayWindowsMessage) {
804                varMessage = (ReplayWindowsMessage) tmp;
805            }
806            else {
807                throw new IllegalArgumentException("Failure obtaining term value for rule " +
808                    currentRuleName + ": No message \"" + obj + "\" stored.");
809            }
810        }
811        return varMessage;
812    }
813
814    /**
815     * <p>
816     * Retrieves a stored message sequence from the storage.
817     * </p>
818     *
819     * @param obj
820     *            object identifier in the storage
821     * @return message sequence retrieved from the storage
822     * @throws IllegalArgumentException
823     *             thrown if no message sequences with the identifier obj is found in the storage
824     */
825    @SuppressWarnings("unchecked")
826    private List<ReplayWindowsMessage> getStoredSeqVariable(String obj)
827        throws IllegalArgumentException
828    {
829        List<ReplayWindowsMessage> varMsgSeq = null;
830        Object tmp = messageStorage.get(obj);
831        if (tmp instanceof List<?>) {
832            varMsgSeq = (List<ReplayWindowsMessage>) tmp;
833        }
834        else {
835            throw new IllegalArgumentException("Failure obtaining term value for rule " +
836                                               currentRuleName + ": No sequence \"" + obj +
837                                               "\" store.");
838        }
839        return varMsgSeq;
840    }
841
842    /**
843     * <p>
844     * convenience method for {@link #getTermValue(WindowsMessage, Term)} with current message is
845     * null.
846     * </p>
847     *
848     * @param termElement
849     *            {@link Element} representing the term node
850     * @return value of the term or {@code null} of the term node could not be evaluated
851     */
852    private <T> T getTermValue(EventGenerationRule.Term term, Class<T> expectedType) {
853        return getTermValue(null, term, expectedType);
854    }
855
856    /**
857     * <p>
858     * Handles term-nodes and returns the value of the described term.
859     * </p>
860     *
861     * @param currentMessage
862     *            current message during the parsing; required to resolve references to "this" in a
863     *            term
864     * @param termElement
865     *            {@link Element} representing the term node
866     * @return value of the term or {@code null} of the term node could not be evaluated
867     */
868    private <T> T getTermValue(WindowsMessage           currentMessage,
869                               EventGenerationRule.Term term,
870                               Class<T>                 expectedType)
871    {
872        T value = null;
873       
874        if ("constValue".equals(term.getName())) {
875            value = getValueAsType(term.getValue(), expectedType);
876        }
877        else if ("paramValue".equals(term.getName())) {
878            String objectName = term.getMessageId();
879            WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName);
880            if (varMessage != null) {
881                String param = term.getMessageParameterName();
882                value = getValueAsType(varMessage.getParameter(param), expectedType);
883            }
884        }
885        else if ("winInfoValue".equals(term.getName())) {
886            String objectName = term.getMessageId();
887            WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName);
888            if (varMessage != null) {
889                String paramString = term.getWindowParameterName();
890                if (paramString.equals("class")) {
891                    value = getValueAsType
892                        (((MFCGUIElement) varMessage.getTarget()).getType(), expectedType);
893                }
894                else if (paramString.equals("resourceId")) {
895                    value = getValueAsType
896                        (((MFCGUIElement) varMessage.getTarget()).getResourceId(), expectedType);
897                }
898                else if (paramString.equals("hwnd")) {
899                    value = getValueAsType
900                        (((MFCGUIElement) varMessage.getTarget()).getId(), expectedType);
901                }
902                else if (paramString.equals("parentTarget")) {
903                    String target = varMessage.getTargetXML();
904                    int index = target.lastIndexOf("<");
905                    if (index == 0) {
906                        Console.traceln(Level.WARNING, "Trying to adress parent of top-level " +
907                                        "window! Replay probably invalid!");
908                    }
909                    value =  getValueAsType(target.substring(0, index), expectedType);
910                }
911                else if (paramString.equals("parentClass")) {
912                    value =  getValueAsType
913                        (((MFCGUIElement) varMessage.getTarget())
914                        .getParent().getSpecification().getType(), expectedType);
915                }
916            }
917        }
918        else if ("msgInfoValue".equals(term.getName())) {
919            String objectName = term.getMessageId();
920            WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName);
921            if (varMessage != null) {
922                String paramString = term.getMessageInfoName();
923                if (paramString.equals("type")) {
924                    value = getValueAsType(varMessage.getType(), expectedType);
925                }
926                else if (paramString.equals("target")) {
927                    value = getValueAsType(varMessage.getTargetXML(), expectedType);
928                }
929            }
930        }
931        else if ("msgInfoValue".equals(term.getName())) {
932            String objectName = term.getMessageId();
933            WindowsMessage varMessage = getStoredMessageVariable(currentMessage, objectName);
934            if (varMessage != null) {
935                String paramString = term.getMessageInfoName();
936                if (paramString.equals("type")) {
937                    value = getValueAsType(varMessage.getType(), expectedType);
938                }
939                else if (paramString.equals("target")) {
940                    value = getValueAsType(varMessage.getTargetXML(), expectedType);
941                }
942            }
943        }
944       
945        return value;
946    }
947   
948    /**
949     * <p>
950     * Convenience method for {@link #getTermValueAsList(WindowsMessage, Term)} with current
951     * message is null.
952     * </p>
953     *
954     * @param termElement
955     *            {@link Element} representing the term node
956     * @return value of the term or {@code null} of the term node could not be evaluated
957     */
958    private <T> List<T> getTermValueAsList(EventGenerationRule.Term term, Class<T> expectedType) {
959        return getTermValueAsList(null, term, expectedType);
960    }
961
962    /**
963     * <p>
964     * Handles term-nodes and returns the value of the described term.
965     * </p>
966     *
967     * @param currentMessage
968     *            current message during the parsing; required to resolve references to "this" in a
969     *            term
970     * @param termElement
971     *            {@link Element} representing the term node
972     * @return value of the term or {@code null} of the term node could not be evaluated
973     */
974    private <T> List<T> getTermValueAsList(WindowsMessage           currentMessage,
975                                           EventGenerationRule.Term term,
976                                           Class<T>                 expectedElementType)
977    {
978        List<T> values = new ArrayList<T>();
979        if ("seqValue".equals(term.getName())) {
980            List<ReplayWindowsMessage> seqVar = getStoredSeqVariable(term.getSequenceId());
981            Object value;
982           
983            for (ReplayWindowsMessage msg : seqVar) {
984                // msg.getParameter returns null, if parameter is not found,
985                // therefore the List can contain null-values
986                value = msg.getParameter(term.getSequenceParameterName());
987                values.add(getValueAsType(value, expectedElementType));
988            }
989        }
990        else {
991            values.add(getTermValue(currentMessage, term, expectedElementType));
992        }
993       
994        return values;
995    }
996   
997    /**
998     * <p>
999     * Resolves term values.
1000     * </p>
1001     *
1002     * @param value value to be resolved
1003     * @param expectedType class defining the expected type
1004     * @return resolved value
1005     */
1006    @SuppressWarnings("unchecked")
1007    private <T> T getValueAsType(Object value, Class<T> expectedType) {
1008        if (expectedType.isInstance(value)) {
1009            return (T) value;
1010        }
1011        else if (value instanceof String) {
1012            try {
1013                if (WindowsMessageType.class.equals(expectedType)) {
1014                    return (T) WindowsMessageType.parseMessageType((String) value);
1015                }
1016                else if (Short.class.equals(expectedType)) {
1017                    return (T) (Short) Short.parseShort((String) value);
1018                }
1019                else if (Long.class.equals(expectedType)) {
1020                    return (T) (Long) Long.parseLong((String) value);
1021                }
1022            }
1023            catch (Exception e) {
1024                // in this case, the value can not be transformed to the expected value. So ignore
1025                // the exception and fall through to the exception thrown anyway
1026            }
1027        }
1028        else if (value instanceof Long) {
1029            try {
1030                if (Short.class.equals(expectedType)) {
1031                    return (T) (Short) ((Long) value).shortValue();
1032                }
1033                else if (String.class.equals(expectedType)) {
1034                    return (T) ((Long) value).toString();
1035                }
1036            }
1037            catch (Exception e) {
1038                // in this case, the value can not be transformed to the expected value. So ignore
1039                // the exception and fall through to the exception thrown anyway
1040            }
1041        }
1042       
1043        throw new IllegalArgumentException("the term value is not of the expected type " +
1044                                           expectedType + " but a " +
1045                                           (value != null ? value.getClass() : "null"));
1046    }
1047
1048    /**
1049     * <p>
1050     * Handles LOWORD and HIWORD child nodes of LPARAM and WPARAM nodes. The returned value is the
1051     * LPARAM/WPARAM value based on the LOWORD and HIWORD.
1052     * </p>
1053     *
1054     * @param param
1055     *            {@link Element} representing the LPARAM/WPARAM node
1056     * @return value of the LPARAM/WPARAM
1057     */
1058    private long loHiWord(EventGenerationRule.Term lword, EventGenerationRule.Term hword) {
1059        return MAKEPARAM(getTermValue(lword, Short.class), getTermValue(hword, Short.class));
1060    }
1061
1062    /**
1063     * <p>
1064     * Takes to short integers and combines them into the high and low order bits of an integer.
1065     * </p>
1066     *
1067     * @param loword
1068     *            low word
1069     * @param hiword
1070     *            high word
1071     * @return combined integer
1072     */
1073    private static int MAKEPARAM(short loword, short hiword) {
1074        return loword | ((int) hiword) << Short.SIZE;
1075    }
1076
1077    /**
1078     * <p>
1079     * Parses the rules.
1080     * </p>
1081     *
1082     */
1083    @SuppressWarnings("unchecked")
1084    private void parseGenerationRules() {
1085        SAXBuilder builder = new SAXBuilder();
1086        Document doc = null;
1087
1088        try {
1089            doc = builder.build(rulesFile);
1090            rulesNamespace = Namespace.getNamespace("ul:rules");
1091        }
1092        catch (JDOMException e) {
1093            Console.printerrln("Invalid rules file.");
1094            Console.logException(e);
1095            return;
1096        }
1097        catch (IOException e) {
1098            Console.printerrln("Invalid rules file.");
1099            Console.logException(e);
1100            return;
1101        }
1102
1103        Element rulesRoot = doc.getRootElement();
1104       
1105        List<Element> ruleElements = rulesRoot.getChildren("rule", rulesNamespace);
1106
1107        generationRules = new ArrayList<EventGenerationRule>();
1108
1109        for (Element ruleElement : ruleElements) {
1110            generationRules.add(new EventGenerationRule(ruleElement, rulesNamespace));
1111        }
1112    }
1113
1114}
Note: See TracBrowser for help on using the repository browser.