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

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

start implementing key event, not working yet
also another id hack for testing purposes - this needs to be improved asap

  • Property svn:mime-type set to text/plain
File size: 17.0 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.Iterator;
25import java.util.List;
26import java.util.UUID;
27import java.util.logging.Level;
28
29import de.ugoe.cs.autoquest.CommandHelpers;
30import de.ugoe.cs.autoquest.SequenceInstanceOf;
31import de.ugoe.cs.util.console.Command;
32import de.ugoe.cs.autoquest.eventcore.Event;
33import de.ugoe.cs.autoquest.eventcore.gui.*;
34import de.ugoe.cs.autoquest.keyboardmaps.VirtualKey;
35import de.ugoe.cs.autoquest.plugin.jfc.guimodel.JFCGUIElement;
36import de.ugoe.cs.util.console.Console;
37import de.ugoe.cs.util.console.GlobalDataContainer;
38
39// helper class for the tree like structure part within a Jacareto file
40class StructureNode {
41    private static int nextRef = 0;
42
43    public String content;
44    public ArrayList<StructureNode> children;
45
46    public StructureNode(String type) {
47        content = "<StructureElement class=\"jacareto.struct." + type + "\">";
48        children = new ArrayList<StructureNode>();
49    }
50
51    public StructureNode() {
52        content = "<Recordable ref=\"" + (nextRef++) + "\" />";
53        children = new ArrayList<StructureNode>();
54    }
55
56    public StructureNode add(String type) {
57        StructureNode node = new StructureNode(type);
58        children.add(node);
59        return node;
60    }
61
62    public void addRecordable() {
63        children.add(new StructureNode());
64    }
65
66    @Override
67    public String toString() {
68        String separator = System.getProperty("line.separator");
69        String result = content + separator;
70
71        for (StructureNode child : children) {
72            result += child.toString();
73        }
74
75        if (content.endsWith("/>")) {
76            return result;
77        }
78        return result + "</StructureElement>" + separator;
79    }
80}
81
82/**
83 * <p>
84 * Command to create a Jacareto xml replay file from stored sessions.
85 * </p>
86 *
87 * @author Daniel May
88 * @version 1.0
89 */
90public class CMDgenerateJacaretoReplay implements Command {
91    private JFCGUIElement currentFocus;
92    private StructureNode structure;
93    private StructureNode lastMouseClickEvent;
94    private StructureNode lastFocusChangeEvent;
95    private StructureNode lastItemActionEvent;
96
97    /*
98     * (non-Javadoc)
99     *
100     * @see de.ugoe.cs.util.console.Command#help()
101     */
102    @Override
103    public String help() {
104        return "generateJacaretoReplay <filename> <sequences>";
105    }
106
107    /*
108     * (non-Javadoc)
109     *
110     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
111     */
112    @SuppressWarnings("unchecked")
113    @Override
114    public void run(List<Object> parameters) {
115        String filename;
116        String sequencesName;
117        try {
118            filename = (String) parameters.get(0);
119            sequencesName = (String) parameters.get(1);
120        }
121        catch (Exception e) {
122            throw new IllegalArgumentException();
123        }
124
125        Collection<List<Event>> sequences = null;
126        Object dataObject = GlobalDataContainer.getInstance().getData(sequencesName);
127        if (dataObject == null) {
128            CommandHelpers.objectNotFoundMessage(sequencesName);
129            return;
130        }
131        if (!SequenceInstanceOf.isCollectionOfSequences(dataObject)) {
132            CommandHelpers.objectNotType(sequencesName, "Collection<List<Event<?>>>");
133            return;
134        }
135
136        sequences = (Collection<List<Event>>) dataObject;
137
138        writeJacaretoXML(sequences, filename);
139    }
140
141    private void writeLine(BufferedWriter writer, String line) throws IOException {
142        writer.write(line);
143        writer.newLine();
144    }
145
146    private void writeJacaretoHead(BufferedWriter writer) throws IOException {
147        writeLine(writer, "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>");
148        writeLine(writer, "<JacaretoStructure>");
149        writeLine(writer, "<Record>");
150
151        // TODO: This header content is basically copy+paste from a
152        // specific jacareto replay file right now.
153        // Some things such as screen resolution and especially the
154        // application starter details need to be changed for general cases.
155        writeLine(writer,
156                  "<Calendar procTime=\"0\" duration=\"0\" year=\"2014\" month=\"8\" date=\"11\" hour=\"14\" min=\"43\" sec=\"41\" uuid=\"06831ba1-f28a-4e05-b46e-ce9d8f9ffa0f\" />");
157        writeLine(writer,
158                  "<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\" />");
159        writeLine(writer,
160                  "<KeyboardState procTime=\"0\" duration=\"0\" isNumLockOn=\"false\" isScrollLockOn=\"false\" isCapsLockOn=\"false\" applyIsNumLockOn=\"true\" applyIsScrollLockOn=\"true\" applyIsCapsLockOn=\"true\" uuid=\"28146f79-9fc7-49f9-b4a8-5866a7625683\" />");
161        writeLine(writer, "<ComponentMode numberPopupMenues=\"true\" />");
162        writeLine(writer,
163                  "<ApplicationStarter procTime=\"0\" duration=\"0\" name=\"HelloWorldSwing\" class=\"HelloWorldSwing\" initclass=\"\" basepath=\"/home/daniel/project/autoquest-jfcmonitor\" classpathext=\"${basepath}/helloswing.jar;${basepath}/.;\" detectDuration=\"false\" captureparams=\"\" replayparams=\"\" uuid=\"a7b7d7b9-caa9-4d6d-b052-cf74d353275e\" />");
164    }
165
166    private void writeJacaretoEvents(BufferedWriter writer, Collection<List<Event>> sequences)
167        throws IOException
168    {
169        structure = new StructureNode("RootElement");
170        // reference the elements that we included in the header
171        structure.addRecordable(); // Calendar
172        structure.addRecordable(); // SystemInfo
173        structure.addRecordable(); // KeyboardState
174        structure.addRecordable(); // ComponentMode
175        structure.addRecordable(); // ApplicationStarter
176
177        for (List<Event> sequence : sequences) {
178            for (Iterator<Event> eventIter = sequence.iterator(); eventIter.hasNext();) {
179                Event event = eventIter.next();
180
181                if (event.getType() instanceof MouseButtonDown) {
182                    lastMouseClickEvent = new StructureNode("MouseClick");
183                    writeMouseClickEvent(writer, event, 501);
184                }
185                else if (event.getType() instanceof MouseButtonUp) {
186                    writeMouseClickEvent(writer, event, 502);
187                }
188                else if (event.getType() instanceof MouseClick) {
189                    writeMouseClickEvent(writer, event, 500);
190                    // FIXME: don't always write an item action
191                    writeItemActionEvent(writer, event);
192                    // FIXME: don't write it all here because there
193                    // might be no item action at all
194                    if (lastFocusChangeEvent == null) {
195                        // write structure sequentially
196                        structure.children.add(lastMouseClickEvent);
197                        structure.children.add(lastItemActionEvent);
198                    }
199                    else {
200                        // with nested structure
201                        structure.children.add(lastItemActionEvent);
202                        lastItemActionEvent.children.add(0, lastFocusChangeEvent);
203                        lastFocusChangeEvent.children.add(0, lastMouseClickEvent);
204
205                        lastFocusChangeEvent = null;
206                    }
207                }
208                else if (event.getType() instanceof KeyboardFocusChange) {
209                    writeFocusChangeEvent(writer, event);
210                }
211                else if (event.getType() instanceof KeyPressed) {
212                    // writeKeyEvent(writer, event, 401);
213                }
214                else if (event.getType() instanceof KeyReleased) {
215                    // writeKeyEvent(writer, event, 402);
216                }
217            }
218        }
219    }
220
221    private void writeJacaretoTail(BufferedWriter writer) throws IOException {
222        writeLine(writer, "</Record>");
223
224        // write the recording's structure
225        writeLine(writer, "<Structure>");
226        writer.write(structure.toString());
227        // close root element
228        writeLine(writer, "</Structure>");
229    }
230
231    private void writeJacaretoXML(Collection<List<Event>> sequences, String filename) {
232        BufferedWriter writer = new BufferedWriter(openReplayFile(filename + ".xml"));
233
234        try {
235            writeJacaretoHead(writer);
236            writeJacaretoEvents(writer, sequences);
237            writeJacaretoTail(writer);
238            writeLine(writer, "</JacaretoStructure>");
239
240            writer.flush();
241            writer.close();
242        }
243        catch (IOException e) {
244            Console.printerrln("Unable to write Jacareto replay file " + filename);
245        }
246    }
247
248    /**
249     * <p>
250     * Helper function that opens the replay file for writing.
251     * </p>
252     *
253     * @param filename
254     *            name and path of the replay file
255     * @param encoding
256     *            file encoding, empty string for platform default
257     * @return {@link OutputStreamWriter} that writes to the replay file
258     */
259    private OutputStreamWriter openReplayFile(String filename) {
260        File file = new File(filename);
261        boolean fileCreated;
262        try {
263            fileCreated = file.createNewFile();
264            if (!fileCreated) {
265                Console.traceln(Level.INFO, "Created logfile " + filename);
266            }
267            else {
268                Console.traceln(Level.INFO, "Overwrote existing logfile " + filename);
269            }
270        }
271        catch (IOException e) {
272            Console.printerrln("Unable to create file " + filename);
273            Console.logException(e);
274        }
275        OutputStreamWriter writer = null;
276        try {
277            writer = new OutputStreamWriter(new FileOutputStream(file));
278        }
279        catch (IOException e) {
280            Console.printerrln("Unable to open file for writing (read-only file):" + filename);
281            Console.logException(e);
282        }
283        return writer;
284    }
285
286    private void writeItemActionEvent(BufferedWriter writer, Event event) throws IOException {
287        JFCGUIElement target = (JFCGUIElement) event.getTarget();
288        MouseButtonInteraction info = (MouseButtonInteraction) event.getType();
289
290        //@formatter:off
291        writeLine(writer,
292            "<ItemEvent "
293            + "procTime=\"0\" "
294            + "duration=\"0\" "
295            + "source=\"" + target.getJacaretoHierarchy() + "\" "
296            + "class=\"" + target.getSpecification().getType() + "\" "
297            + "uuid=\"" + UUID.randomUUID() + "\" "
298            + "ID=\"701\" "
299            + "item=\"\" "
300            + "stateChange=\"1\" />"
301        );
302        writeLine(writer,
303            "<ActionEvent "
304            + "procTime=\"0\" "
305            + "duration=\"0\" "
306            + "source=\"" + target.getJacaretoHierarchy() + "\" "
307            + "class=\"" + target.getSpecification().getType() + "\" "
308            + "uuid=\"" + UUID.randomUUID() + "\" "
309            + "ID=\"1001\" "
310            + "command=" + target.getName() + " "
311            + "modifiers=\"" + getButtonModifier(info) + "\" />"
312        );
313        //@formatter:on
314        lastItemActionEvent = new StructureNode("ItemStateChange");
315        lastItemActionEvent.addRecordable();
316        lastItemActionEvent.addRecordable();
317    }
318
319    private void writeFocusChangeEvent(BufferedWriter writer, Event event) throws IOException {
320        KeyboardFocusChange info = (KeyboardFocusChange) event.getType();
321        JFCGUIElement target = (JFCGUIElement) event.getTarget();
322
323        if (currentFocus != null) {
324            lastFocusChangeEvent = new StructureNode("FocusChange");
325
326            // focus lost on old target
327            writeFocusEvent(writer, info, currentFocus, 1005);
328            // focus gained on new target
329            writeFocusEvent(writer, info, target, 1004);
330        }
331        else {
332            // TODO: it seems like Jacareto wants a window activation before
333            // the first focus event but that is not the case in autoquest,
334            // skip for now
335        }
336
337        currentFocus = target;
338    }
339
340    private void writeFocusEvent(BufferedWriter writer,
341                                 KeyboardFocusChange info,
342                                 JFCGUIElement target,
343                                 int jacId) throws IOException
344    {
345        //@formatter:off
346        writeLine(writer,
347            "<FocusEvent "
348            + "procTime=\"0\" "
349            + "duration=\"0\" "
350            + "source=\"" + target.getJacaretoHierarchy() + "\" "
351            + "class=\"" + target.getSpecification().getType() + "\" "
352            + "uuid=\"" + UUID.randomUUID() + "\" "
353            + "ID=\"" + jacId + "\" "
354            + "component=\"null\" "
355            + "root=\"" + target.getJacaretoRoot() + "\" "
356            + "xPos=\"0\" "
357            + "yPos=\"0\" "
358            + "width=\"0\" "
359            + "height=\"0\" "
360            + "isTemporary=\"false\" />"
361        );
362        //@formatter:on
363        lastFocusChangeEvent.addRecordable();
364    }
365
366    private void writeMouseClickEvent(BufferedWriter writer, Event event, int jacId)
367        throws IOException
368    {
369        MouseButtonInteraction info = (MouseButtonInteraction) event.getType();
370        JFCGUIElement target = (JFCGUIElement) event.getTarget();
371        int clickCount = event.getType() instanceof MouseDoubleClick ? 2 : 1;
372
373        // TODO: change procTime and duration to adequate values
374        //@formatter:off
375        writeLine(writer,
376            "<MouseEvent "
377            + "procTime=\"0\" "
378            + "duration=\"150\" "
379            + "source=\"" + target.getJacaretoHierarchy() + "\" "
380            + "class=\"" + target.getSpecification().getType() + "\" "
381            + "uuid=\"" + UUID.randomUUID() + "\" "
382            + "ID=\"" + jacId + "\" "
383            + "component=\"null\" "
384            + "root=\"" + target.getJacaretoRoot() + "\" "
385            + "xPos=\"0\" "
386            + "yPos=\"0\" "
387            + "width=\"0\" "
388            + "height=\"0\" "
389            + "when=\"" + event.getTimestamp() + "\" "
390            + "isConsumed=\"false\">"
391        );
392        writeLine(writer,
393            "<MouseInfo "
394            + "xPosition=\"" + info.getX() + "\" "
395            + "yPosition=\"" + info.getY() + "\" "
396            + "rootX=\"0\" "
397            + "rootY=\"0\" "
398            + "clickCount=\"" + clickCount + "\" "
399            + "modifiers=\"" + getButtonModifier(info) + "\" "
400            + "isPopupTrigger=\"false\" />"
401        );
402        writeLine(writer, "</MouseEvent>");
403        //@formatter:on
404
405        lastMouseClickEvent.addRecordable();
406    }
407
408    private int getButtonModifier(MouseButtonInteraction info) {
409        switch (info.getButton())
410        {
411            case LEFT:
412                return 16;
413            case MIDDLE:
414                return 8;
415            case RIGHT:
416                return 4;
417            default:
418                // TODO: handle unknown mouse button
419                return -1;
420        }
421    }
422
423    private void writeKeyEvent(BufferedWriter writer, Event event, int jacId) throws IOException {
424        KeyInteraction info = (KeyInteraction) event.getType();
425        JFCGUIElement target = (JFCGUIElement) event.getTarget();
426
427        //@formatter:off
428        writeLine(writer,
429            "<KeyEvent "
430            + "procTime=\"0\" "
431            + "duration=\"150\" "
432            + "source=\"" + target.getJacaretoHierarchy() + "\" "
433            + "class=\"" + target.getSpecification().getType() + "\" "
434            + "uuid=\"" + UUID.randomUUID() + "\" "
435            + "ID=\"" + jacId + "\" "
436            + "component=\"null\" "
437            + "root=\"" + target.getJacaretoRoot() + "\" "
438            + "xPos=\"0\" "
439            + "yPos=\"0\" "
440            + "width=\"0\" "
441            + "height=\"0\" "
442            + "when=\"" + event.getTimestamp() + "\" "
443            + "isConsumed=\"false\">"
444        );
445        writeLine(writer,
446            "<KeyInfo "
447            + "keyCode=\"" + getKeyCode(info.getKey()) + "\" "
448            + "keyChar=\"" + getKeyChar(info.getKey()) + "\" "
449            + "modifiers=\"0\" />"
450        );
451       
452        writeLine(writer, "</KeyEvent>");
453       
454        // TODO: keyevent structure part
455    }
456   
457    private String getKeyChar (VirtualKey key) {
458        // TODO
459        return "";
460    }
461   
462    private int getKeyCode (VirtualKey key) {
463        // TODO
464        return -1;
465    }
466}
Note: See TracBrowser for help on using the repository browser.