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

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