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

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