source: trunk/EventBenchConsole/src/de/ugoe/cs/eventbench/windows/EventGenerator.java @ 171

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