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

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