source: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/commands/CMDgenerateJacaretoReplay.java @ 1713

Last change on this file since 1713 was 1713, checked in by dmay, 10 years ago

this class variable needs to be resettet between runs

  • Property svn:mime-type set to text/plain
File size: 19.4 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.plugin.jfc.commands;
16
17import java.io.BufferedWriter;
18import java.io.File;
19import java.io.FileOutputStream;
20import java.io.IOException;
21import java.io.OutputStreamWriter;
22import java.util.ArrayList;
23import java.util.Collection;
24import java.util.HashMap;
25import java.util.Iterator;
26import java.util.List;
27import java.util.UUID;
28import java.util.logging.Level;
29
30import de.ugoe.cs.autoquest.CommandHelpers;
31import de.ugoe.cs.autoquest.SequenceInstanceOf;
32import de.ugoe.cs.util.console.Command;
33import de.ugoe.cs.autoquest.eventcore.Event;
34import de.ugoe.cs.autoquest.eventcore.gui.*;
35import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
36import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement;
37import de.ugoe.cs.util.console.Console;
38import de.ugoe.cs.util.console.GlobalDataContainer;
39
40// helper class for the tree like structure part within a Jacareto file
41class StructureNode {
42    public static int nextRef = 0;
43
44    public String content;
45    public ArrayList<StructureNode> children;
46
47    public StructureNode(String type) {
48        content = "<StructureElement class=\"jacareto.struct." + type + "\">";
49        children = new ArrayList<StructureNode>();
50    }
51
52    public StructureNode() {
53        content = "<Recordable ref=\"" + (nextRef++) + "\" />";
54        children = new ArrayList<StructureNode>();
55    }
56
57    public StructureNode add(String type) {
58        StructureNode node = new StructureNode(type);
59        children.add(node);
60        return node;
61    }
62
63    public void addRecordable() {
64        children.add(new StructureNode());
65    }
66
67    @Override
68    public String toString() {
69        String separator = System.getProperty("line.separator");
70        String result = content + separator;
71
72        for (StructureNode child : children) {
73            result += child.toString();
74        }
75
76        if (content.endsWith("/>")) {
77            return result;
78        }
79        return result + "</StructureElement>" + separator;
80    }
81}
82
83/**
84 * <p>
85 * Command to create a Jacareto xml replay file from stored sessions.
86 * </p>
87 *
88 * @author Daniel May
89 * @version 1.0
90 */
91public class CMDgenerateJacaretoReplay implements Command {
92    private JFCGUIElement currentFocus;
93    private StructureNode structure;
94
95    private StructureNode lastKeySequenceEvent;
96    private StructureNode lastKeyTypedEvent;
97    private int currentKeyModifiers;
98
99    private HashMap<VirtualKey, Integer> modifiers;
100
101    private StructureNode lastMouseClickEvent;
102    private StructureNode lastFocusChangeEvent;
103    private StructureNode lastItemActionEvent;
104
105    /*
106     * (non-Javadoc)
107     *
108     * @see de.ugoe.cs.util.console.Command#help()
109     */
110    @Override
111    public String help() {
112        return "generateJacaretoReplay <filename> <sequences> <class> <basepath> <classpathext> {<initclass>}";
113    }
114
115    /*
116     * (non-Javadoc)
117     *
118     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
119     */
120    @SuppressWarnings("unchecked")
121    @Override
122    public void run(List<Object> parameters) {
123        String filename;
124        String sequencesName;
125        String classpath;
126        String basepath;
127        String classpathext;
128        String initclass = "";
129        try {
130            filename = (String) parameters.get(0);
131            sequencesName = (String) parameters.get(1);
132            classpath = (String) parameters.get(2);
133            basepath = (String) parameters.get(3);
134            classpathext = (String) parameters.get(4);
135        }
136        catch (Exception e) {
137            throw new IllegalArgumentException();
138        }
139
140        if (parameters.size() > 5) {
141            initclass = (String) parameters.get(5);
142        }
143
144        Collection<List<Event>> sequences = null;
145        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
146        if (dataObject == null) {
147            CommandHelpers.objectNotFoundMessage(sequencesName);
148            return;
149        }
150        if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
151            CommandHelpers.objectNotType(sequencesName, "Collection<List<Event<?>>>");
152            return;
153        }
154
155        sequences = (Collection<List<Event>>) dataObject;
156
157        // map which maps VirtualKeys back to awt key modifier codes
158        modifiers = new HashMap<>();
159        modifiers.put(VirtualKey.SHIFT, 1);
160        modifiers.put(VirtualKey.CONTROL, 2);
161        modifiers.put(VirtualKey.ALT, 8);
162        modifiers.put(VirtualKey.ALT_GRAPH, 32);
163        currentKeyModifiers = 0;
164
165        StructureNode.nextRef = 0;
166
167        writeJacaretoXML(sequences, filename, classpath, initclass, basepath, classpathext);
168    }
169
170    private void writeLine(BufferedWriter writer, String line) throws IOException {
171        writer.write(line);
172        writer.newLine();
173    }
174
175    private void writeJacaretoHead(BufferedWriter writer,
176                                   String classname,
177                                   String initclass,
178                                   String basepath,
179                                   String classpathext) throws IOException
180    {
181        writeLine(writer, "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>");
182        writeLine(writer, "<JacaretoStructure>");
183        writeLine(writer, "<Record>");
184
185        // TODO: This header content is basically copy+paste from a
186        // specific jacareto replay file right now.
187        writeLine(writer,
188                  "<Calendar procTime=\"0\" duration=\"0\" year=\"2000\" month=\"1\" date=\"11\" hour=\"1\" min=\"1\" sec=\"1\" uuid=\"06831ba1-f28a-4e05-b46e-ce9d8f9ffa0f\" />");
189        writeLine(writer,
190                  "<SystemInfo procTime=\"0\" duration=\"0\" screenWidth=\"2646\" screenHeight=\"1024\" javaVersion=\"1.7.0_65\" lookAndFeel=\"javax.swing.plaf.metal.MetalLookAndFeel\" uuid=\"720f430f-52cf-4d8b-9fbe-58434f766efe\" />");
191        writeLine(writer,
192                  "<KeyboardState procTime=\"0\" duration=\"0\" isNumLockOn=\"false\" isScrollLockOn=\"false\" isCapsLockOn=\"false\" applyIsNumLockOn=\"true\" applyIsScrollLockOn=\"true\" applyIsCapsLockOn=\"true\" uuid=\"28146f79-9fc7-49f9-b4a8-5866a7625683\" />");
193        writeLine(writer, "<ComponentMode numberPopupMenues=\"true\" />");
194        //@formatter:off
195        writeLine(writer, "<ApplicationStarter "
196            + "procTime=\"0\" "
197            + "duration=\"0\" "
198            + "name=\"Autoquest Replay\" "
199            + "class=\"" + classname + "\" "
200            + "initclass=\"" + initclass + "\" "
201            + "basepath=\"" + basepath + "\" "
202            + "classpathext=\"" + classpathext + "\" "
203            + "detectDuration=\"false\" "
204            + "captureparams=\"\" "
205            + "replayparams=\"\" "
206            + "uuid=\"a7b7d7b9-caa9-4d6d-b052-cf74d353275e\" />"
207        );
208        //@formatter:on
209    }
210
211    private void writeJacaretoEvents(BufferedWriter writer, Collection<List<Event>> sequences)
212        throws IOException
213    {
214        structure = new StructureNode("RootElement");
215        // reference the elements that we included in the header
216        structure.addRecordable(); // Calendar
217        structure.addRecordable(); // SystemInfo
218        structure.addRecordable(); // KeyboardState
219        structure.addRecordable(); // ComponentMode
220        structure.addRecordable(); // ApplicationStarter
221
222        for (List<Event> sequence : sequences) {
223            for (Iterator<Event> eventIter = sequence.iterator(); eventIter.hasNext();) {
224                Event event = eventIter.next();
225
226                if (event.getType() instanceof MouseButtonDown) {
227                    lastKeySequenceEvent = null;
228
229                    lastMouseClickEvent = new StructureNode("MouseClick");
230                    writeMouseClickEvent(writer, event, 501);
231                }
232                else if (event.getType() instanceof MouseButtonUp) {
233                    lastKeySequenceEvent = null;
234
235                    writeMouseClickEvent(writer, event, 502);
236                }
237                else if (event.getType() instanceof MouseClick) {
238                    lastKeySequenceEvent = null;
239
240                    writeMouseClickEvent(writer, event, 500);
241                    // FIXME: don't always write an item action
242                    writeItemActionEvent(writer, event);
243                    // FIXME: don't write it all here because there
244                    // might be no item action at all
245                    if (lastFocusChangeEvent == null) {
246                        // write structure sequentially
247                        structure.children.add(lastMouseClickEvent);
248                        structure.children.add(lastItemActionEvent);
249                    }
250                    else {
251                        // with nested structure
252                        structure.children.add(lastItemActionEvent);
253                        lastItemActionEvent.children.add(0, lastFocusChangeEvent);
254                        lastFocusChangeEvent.children.add(0, lastMouseClickEvent);
255
256                        lastFocusChangeEvent = null;
257                    }
258                }
259                else if (event.getType() instanceof KeyboardFocusChange) {
260                    lastKeySequenceEvent = null;
261
262                    writeFocusChangeEvent(writer, event);
263                }
264                else if (event.getType() instanceof KeyPressed) {
265                    if (lastKeySequenceEvent == null) {
266                        lastKeySequenceEvent = structure.add("KeySequence");
267                    }
268                    lastKeyTypedEvent = lastKeySequenceEvent.add("KeyTyped");
269                    writeKeyEvent(writer, event, 401);
270                }
271                else if (event.getType() instanceof KeyReleased) {
272                    writeKeyEvent(writer, event, 402);
273                }
274            }
275        }
276    }
277
278    private void writeJacaretoTail(BufferedWriter writer) throws IOException {
279        writeLine(writer, "</Record>");
280
281        // write the recording's structure
282        writeLine(writer, "<Structure>");
283        writer.write(structure.toString());
284        // close root element
285        writeLine(writer, "</Structure>");
286    }
287
288    private void writeJacaretoXML(Collection<List<Event>> sequences,
289                                  String filename,
290                                  String classpath,
291                                  String initclass,
292                                  String basepath,
293                                  String classpathext)
294    {
295        BufferedWriter writer = new BufferedWriter(openReplayFile(filename + ".xml"));
296
297        try {
298            writeJacaretoHead(writer, classpath, initclass, basepath, classpathext);
299            writeJacaretoEvents(writer, sequences);
300            writeJacaretoTail(writer);
301            writeLine(writer, "</JacaretoStructure>");
302
303            writer.flush();
304            writer.close();
305        }
306        catch (IOException e) {
307            Console.printerrln("Unable to write Jacareto replay file " + filename);
308        }
309    }
310
311    /**
312     * <p>
313     * Helper function that opens the replay file for writing.
314     * </p>
315     *
316     * @param filename
317     *            name and path of the replay file
318     * @param encoding
319     *            file encoding, empty string for platform default
320     * @return {@link OutputStreamWriter} that writes to the replay file
321     */
322    private OutputStreamWriter openReplayFile(String filename) {
323        File file = new File(filename);
324        boolean fileCreated;
325        try {
326            fileCreated = file.createNewFile();
327            if (!fileCreated) {
328                Console.traceln(Level.INFO, "Created logfile " + filename);
329            }
330            else {
331                Console.traceln(Level.INFO, "Overwrote existing logfile " + filename);
332            }
333        }
334        catch (IOException e) {
335            Console.printerrln("Unable to create file " + filename);
336            Console.logException(e);
337        }
338        OutputStreamWriter writer = null;
339        try {
340            writer = new OutputStreamWriter(new FileOutputStream(file));
341        }
342        catch (IOException e) {
343            Console.printerrln("Unable to open file for writing (read-only file):" + filename);
344            Console.logException(e);
345        }
346        return writer;
347    }
348
349    private void writeItemActionEvent(BufferedWriter writer, Event event) throws IOException {
350        JFCGUIElement target = (JFCGUIElement) event.getTarget();
351        MouseButtonInteraction info = (MouseButtonInteraction) event.getType();
352
353        //@formatter:off
354        writeLine(writer,
355            "<ItemEvent "
356            + "procTime=\"0\" "
357            + "duration=\"0\" "
358            + "source=\"" + target.getJacaretoHierarchy() + "\" "
359            + "class=\"" + target.getSpecification().getType() + "\" "
360            + "uuid=\"" + UUID.randomUUID() + "\" "
361            + "ID=\"701\" "
362            + "item=\"\" "
363            + "stateChange=\"1\" />"
364        );
365        writeLine(writer,
366            "<ActionEvent "
367            + "procTime=\"0\" "
368            + "duration=\"0\" "
369            + "source=\"" + target.getJacaretoHierarchy() + "\" "
370            + "class=\"" + target.getSpecification().getType() + "\" "
371            + "uuid=\"" + UUID.randomUUID() + "\" "
372            + "ID=\"1001\" "
373            + "command=" + target.getName() + " "
374            + "modifiers=\"" + getButtonModifier(info) + "\" />"
375        );
376        //@formatter:on
377        lastItemActionEvent = new StructureNode("ItemStateChange");
378        lastItemActionEvent.addRecordable();
379        lastItemActionEvent.addRecordable();
380    }
381
382    private void writeFocusChangeEvent(BufferedWriter writer, Event event) throws IOException {
383        KeyboardFocusChange info = (KeyboardFocusChange) event.getType();
384        JFCGUIElement target = (JFCGUIElement) event.getTarget();
385
386        if (currentFocus != null) {
387            lastFocusChangeEvent = new StructureNode("FocusChange");
388
389            // focus lost on old target
390            writeFocusEvent(writer, info, currentFocus, 1005);
391            // focus gained on new target
392            writeFocusEvent(writer, info, target, 1004);
393        }
394        else {
395            // TODO: it seems like Jacareto wants a window activation before
396            // the first focus event but that is not the case in autoquest,
397            // skip for now
398        }
399
400        currentFocus = target;
401    }
402
403    private void writeFocusEvent(BufferedWriter writer,
404                                 KeyboardFocusChange info,
405                                 JFCGUIElement target,
406                                 int jacId) throws IOException
407    {
408        //@formatter:off
409        writeLine(writer,
410            "<FocusEvent "
411            + "procTime=\"0\" "
412            + "duration=\"0\" "
413            + "source=\"" + target.getJacaretoHierarchy() + "\" "
414            + "class=\"" + target.getSpecification().getType() + "\" "
415            + "uuid=\"" + UUID.randomUUID() + "\" "
416            + "ID=\"" + jacId + "\" "
417            + "component=\"null\" "
418            + "root=\"" + target.getJacaretoRoot() + "\" "
419            + "xPos=\"0\" "
420            + "yPos=\"0\" "
421            + "width=\"0\" "
422            + "height=\"0\" "
423            + "isTemporary=\"false\" />"
424        );
425        //@formatter:on
426        lastFocusChangeEvent.addRecordable();
427    }
428
429    private void writeMouseClickEvent(BufferedWriter writer, Event event, int jacId)
430        throws IOException
431    {
432        MouseButtonInteraction info = (MouseButtonInteraction) event.getType();
433        JFCGUIElement target = (JFCGUIElement) event.getTarget();
434        int clickCount = event.getType() instanceof MouseDoubleClick ? 2 : 1;
435
436        // TODO: change procTime and duration to adequate values
437        //@formatter:off
438        writeLine(writer,
439            "<MouseEvent "
440            + "procTime=\"0\" "
441            + "duration=\"150\" "
442            + "source=\"" + target.getJacaretoHierarchy() + "\" "
443            + "class=\"" + target.getSpecification().getType() + "\" "
444            + "uuid=\"" + UUID.randomUUID() + "\" "
445            + "ID=\"" + jacId + "\" "
446            + "component=\"null\" "
447            + "root=\"" + target.getJacaretoRoot() + "\" "
448            + "xPos=\"0\" "
449            + "yPos=\"0\" "
450            + "width=\"0\" "
451            + "height=\"0\" "
452            + "when=\"" + event.getTimestamp() + "\" "
453            + "isConsumed=\"false\">"
454        );
455        writeLine(writer,
456            "<MouseInfo "
457            + "xPosition=\"" + info.getX() + "\" "
458            + "yPosition=\"" + info.getY() + "\" "
459            + "rootX=\"0\" "
460            + "rootY=\"0\" "
461            + "clickCount=\"" + clickCount + "\" "
462            + "modifiers=\"" + getButtonModifier(info) + "\" "
463            + "isPopupTrigger=\"false\" />"
464        );
465        writeLine(writer, "</MouseEvent>");
466        //@formatter:on
467
468        lastMouseClickEvent.addRecordable();
469    }
470
471    private int getButtonModifier(MouseButtonInteraction info) {
472        switch (info.getButton())
473        {
474            case LEFT:
475                return 16;
476            case MIDDLE:
477                return 8;
478            case RIGHT:
479                return 4;
480            default:
481                // TODO: handle unknown mouse button
482                return -1;
483        }
484    }
485
486    private void writeKeyEvent(BufferedWriter writer, Event event, int jacId) throws IOException {
487        KeyInteraction info = (KeyInteraction) event.getType();
488        JFCGUIElement target = (JFCGUIElement) event.getTarget();
489        int keyCode = info.getKey().getVirtualKeyCode();
490
491        applyKeyModifier(info.getKey(), jacId == 401);
492
493        //@formatter:off
494        writeLine(writer,
495            "<KeyEvent "
496            + "procTime=\"0\" "
497            + "duration=\"150\" "
498            + "source=\"" + target.getJacaretoHierarchy() + "\" "
499            + "class=\"" + target.getSpecification().getType() + "\" "
500            + "uuid=\"" + UUID.randomUUID() + "\" "
501            + "ID=\"" + jacId + "\" "
502            + "component=\"null\" "
503            + "root=\"" + target.getJacaretoRoot() + "\" "
504            + "xPos=\"0\" "
505            + "yPos=\"0\" "
506            + "width=\"0\" "
507            + "height=\"0\" "
508            + "when=\"" + event.getTimestamp() + "\" "
509            + "isConsumed=\"false\">"
510        );
511        writeLine(writer,
512            "<KeyInfo "
513            + "keyCode=\"" + keyCode + "\" "
514            + "keyChar=\"" + getKeyChar(keyCode) + "\" "
515            + "modifiers=\"" + currentKeyModifiers + "\" />"
516        );
517       
518        writeLine(writer, "</KeyEvent>");
519       
520        lastKeyTypedEvent.addRecordable();
521    }
522   
523    private String getKeyChar (int keyCode) {
524        if (keyCode >= 32 && keyCode < 127) {
525            return String.valueOf((char)keyCode);
526        }
527        return "_NO_LEGAL_XML_CHAR";
528    }
529   
530    private void applyKeyModifier (VirtualKey key, boolean set) {
531        Integer modifier = modifiers.get(key);
532        if (modifier != null) {
533            if (set) {
534                currentKeyModifiers |= modifier;
535            }
536            else {
537                currentKeyModifiers &= ~modifier;
538            }
539        }
540    }
541}
Note: See TracBrowser for help on using the repository browser.