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

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