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

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

refactorings

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