source: trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/HTMLLogParser.java @ 975

Last change on this file since 975 was 975, checked in by pharms, 12 years ago
  • prevented discarding unfinished sequences and GUI models
File size: 13.9 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.html;
16
17import java.io.BufferedReader;
18import java.io.File;
19import java.io.FileInputStream;
20import java.io.FileNotFoundException;
21import java.io.IOException;
22import java.io.InputStreamReader;
23import java.net.MalformedURLException;
24import java.net.URL;
25import java.util.ArrayList;
26import java.util.Collection;
27import java.util.List;
28import java.util.logging.Level;
29import java.util.regex.Matcher;
30import java.util.regex.Pattern;
31
32import de.ugoe.cs.autoquest.eventcore.Event;
33import de.ugoe.cs.autoquest.eventcore.gui.IInteraction;
34import de.ugoe.cs.autoquest.eventcore.gui.KeyboardFocusChange;
35import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction;
36import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
37import de.ugoe.cs.autoquest.eventcore.gui.Scroll;
38import de.ugoe.cs.autoquest.eventcore.gui.TextInput;
39import de.ugoe.cs.autoquest.eventcore.guimodel.GUIElementFactory;
40import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModel;
41import de.ugoe.cs.autoquest.eventcore.guimodel.GUIModelException;
42import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
43import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElementSpec;
44import de.ugoe.cs.autoquest.eventcore.guimodel.ITextArea;
45import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
46import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLPageElementSpec;
47import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLPageSpec;
48import de.ugoe.cs.util.FileTools;
49import de.ugoe.cs.util.console.Console;
50
51/**
52 * <p>
53 * TODO comment
54 * </p>
55 *
56 * @author Patrick Harms
57 */
58public class HTMLLogParser {
59
60    /**
61     * <p>
62     * Name and path of the robot filter.
63     * </p>
64     */
65    private static final String ROBOTFILTERFILE = "data/robots/robotfilter.txt";
66
67    /**
68     * <p>
69     * Field that contains a regular expression that matches all robots
70     * contained in {@link #ROBOTFILTERFILE}.
71     * </p>
72     */
73    private String robotRegex = null;
74
75    /**
76     *
77     */
78    private Pattern htmlElementPattern = Pattern.compile("(\\w+)(\\[(\\d+)\\]|\\(id=([\\w-]+)\\))");
79
80    /**
81     *
82     */
83    private List<List<Event>> sequences = new ArrayList<List<Event>>();
84   
85    /**
86     *
87     */
88    private GUIModel guiModel = new GUIModel();
89   
90    /**
91     * <p>
92     * TODO: comment
93     * </p>
94     *
95     * @param source
96     */
97    public void parseFile(String source) throws IllegalArgumentException {
98        if (source == null) {
99            throw new IllegalArgumentException("source must not be null");
100        }
101
102        parseFile(new File(source));
103    }
104
105    /**
106     * <p>
107     * TODO: comment
108     * </p>
109     *
110     * @param source
111     */
112    public void parseFile(File source) throws IllegalArgumentException {
113        if (source == null) {
114            throw new IllegalArgumentException("source must not be null");
115        }
116        else if (!source.exists()) {
117            throw new IllegalArgumentException("source file does not exist");
118        }
119        else if (!source.isFile()) {
120            throw new IllegalArgumentException("source is not a file");
121        }
122
123        BufferedReader reader = null;
124       
125        try {
126            reader =
127                new BufferedReader(new InputStreamReader(new FileInputStream(source), "UTF-8"));
128       
129            loadRobotRegex();
130
131            List<Event> sequence = new ArrayList<Event>();
132
133            int lineCounter = 0;
134            String line = reader.readLine();
135            while ((line != null) && (!"".equals(line))) {
136                lineCounter++;
137                String[] values = line.substring(1, line.length() - 1).split("\" \"");
138
139                String clientId = values[0];
140                long timestamp = Long.parseLong(values[1]);
141                String title = values[2];
142                String uriString = values[3];
143                String agent = values[4];
144                String eventName = values[5];
145                String htmlElementPath = values[6];
146
147                List<String> eventParameters = null;
148                if (values.length > 7) {
149                    eventParameters = new ArrayList<String>();
150
151                    for (int i = 7; i < values.length; i++) {
152                        eventParameters.add(values[i]);
153                    }
154                }
155               
156                if (isRobot(agent)) {
157                    // do not handle sessions of robots
158                    Console.println("ignoring robot session: " + agent);
159                    break;
160                }
161               
162               
163                try {
164                    URL url = new URL(uriString);
165                    IGUIElement guiElement = getGUIElement(url, title, htmlElementPath);
166                    IInteraction interaction =
167                        getInteraction(eventName, guiElement, eventParameters);
168                   
169                    if (interaction != null) {
170                        Event event =
171                            createEvent(clientId, interaction, guiElement, timestamp, agent);
172                        sequence.add(event);
173                    }
174                }
175                catch (MalformedURLException e) {
176                    Console.traceln(Level.FINE, "Ignored line " + lineCounter + ": " +
177                                    e.getMessage());
178                }
179               
180                line = reader.readLine();
181            }
182           
183            Console.traceln(Level.INFO, "read user sequence with " + sequence.size() +
184                            " events from " + source);
185           
186            sequences.add(sequence);
187        }
188        catch (Exception e) {
189            Console.printerrln("could not parse file " + source + ": " + e);
190            e.printStackTrace();
191        }
192    }
193
194    /**
195     * <p>
196     * TODO: comment
197     * </p>
198     *
199     * @return
200     */
201    public Collection<List<Event>> getSequences() {
202        return sequences;
203    }
204
205    /**
206     * <p>
207     * TODO: comment
208     * </p>
209     *
210     * @return
211     */
212    public GUIModel getGuiModel() {
213        return guiModel;
214    }
215
216    /**
217     * <p>
218     * Reads {@link #ROBOTFILTERFILE} and creates a regular expression that
219     * matches all the robots defined in the file. The regular expression is
220     * stored in the field {@link #robotRegex}.
221     * </p>
222     *
223     * @throws IOException
224     *             thrown if there is a problem reading the robot filter
225     * @throws FileNotFoundException
226     *             thrown if the robot filter is not found
227     */
228    private void loadRobotRegex() throws IOException, FileNotFoundException {
229        String[] lines = FileTools.getLinesFromFile(ROBOTFILTERFILE);
230        StringBuilder regex = new StringBuilder();
231        for (int i = 0; i < lines.length; i++) {
232            regex.append("(.*" + lines[i] + ".*)");
233            if (i != lines.length - 1) {
234                regex.append('|');
235            }
236        }
237        robotRegex = regex.toString();
238    }
239
240    /**
241     * <p>
242     * TODO: comment
243     * </p>
244     *
245     * @param htmlElementPath
246     * @return
247     * @throws GUIModelException
248     * @throws 
249     */
250    private IGUIElement getGUIElement(URL url, String title, String htmlElementPath)
251        throws GUIModelException
252    {
253        String[] pathElements = htmlElementPath.split("/");
254        List<IGUIElementSpec> guiElementPath = new ArrayList<IGUIElementSpec>();
255       
256        HTMLPageSpec page = new HTMLPageSpec(url, title);
257       
258        guiElementPath.add(page.getServer());
259        guiElementPath.add(page);
260       
261        for (String pathElement : pathElements) {
262            if ((pathElement != null) && (!"".equals(pathElement))) {           
263                Matcher matcher = htmlElementPattern.matcher(pathElement);
264                if (!matcher.matches()) {
265                    throw new IllegalArgumentException("could not parse HTML element " + pathElement);
266                }
267
268                String type = matcher.group(1);
269                String indexStr = matcher.group(3);
270                String id = matcher.group(4);
271
272                int index = -1;
273
274                if ((indexStr != null) && (!"".equals(indexStr))) {
275                    index = Integer.parseInt(indexStr);
276                }
277
278                guiElementPath.add(new HTMLPageElementSpec(page, type, id, index));
279            }
280        }
281       
282        return guiModel.integratePath(guiElementPath, GUIElementFactory.getInstance());
283    }
284
285    /**
286     * <p>
287     * TODO: comment
288     * </p>
289     *
290     * @param eventName
291     * @param guiElement
292     * @param eventParameters
293     * @return
294     */
295    private IInteraction getInteraction(String       eventName,
296                                        IGUIElement  guiElement,
297                                        List<String> eventParameters)
298    {
299        IInteraction result = null;
300       
301        if ("onclick".equals(eventName)) {
302            int[] coordinates =
303                getCoordinateParameter(eventName, eventParameters, 0, "click coordinates");
304            result = new MouseClick
305                (MouseButtonInteraction.Button.LEFT, coordinates[0], coordinates[1]);
306        }
307        else if ("onfocus".equals(eventName)) {
308            result = new KeyboardFocusChange();
309        }
310        else if ("onscroll".equals(eventName)) {
311            int[] coordinates =
312                getCoordinateParameter(eventName, eventParameters, 0, "click coordinates");
313            result = new Scroll(coordinates[0], coordinates[1]);
314        }
315        else if ("onchange".equals(eventName)) {
316            String value = getStringParameter(eventName, eventParameters, 0, "selected value");
317           
318            if ((guiElement instanceof ITextArea) || (guiElement instanceof ITextField)) {
319                result = new TextInput(value, null);
320            }
321            else {
322                throw new IllegalArgumentException("can not handle onchange events on GUI " +
323                                                   "elements of type " + guiElement.getClass());
324            }
325        }
326        else if ("onunload".equals(eventName) || "onbeforeunload".equals(eventName) ||
327                 "onpagehide".equals(eventName) || "onpageshow".equals(eventName))
328        {
329            Console.traceln(Level.FINE, "Ignored event name \"" + eventName + "\"");
330        }
331        else {
332            throw new IllegalArgumentException("unknown event name: \"" + eventName + "\"");
333        }
334       
335        return result;
336    }
337
338    /**
339     * <p>
340     * TODO: comment
341     * </p>
342     *
343     * @param clientId
344     * @param interaction
345     * @param guiElement
346     * @param timestamp
347     * @param agent
348     * @return
349     */
350    private Event createEvent(String       clientId,
351                              IInteraction interaction,
352                              IGUIElement  guiElement,
353                              long         timestamp,
354                              String       agent)
355    {
356        Event event = new Event(interaction, guiElement);
357        event.setParameter("clientId", clientId);
358        event.setParameter("timestamp", Long.toString(timestamp));
359        event.setParameter("agent", agent);
360       
361        return event;
362    }
363
364    /**
365     * <p>
366     * TODO: comment
367     * </p>
368     *
369     * @param eventName
370     * @param eventParameters
371     * @param i
372     * @param string
373     * @return
374     */
375    private String getStringParameter(String       eventName,
376                                      List<String> eventParameters,
377                                      int          parameterIndex,
378                                      String       parameterDesc)
379        throws IllegalArgumentException
380    {
381        String value =
382            eventParameters.size() > parameterIndex ? eventParameters.get(parameterIndex) : null;
383       
384        if (value == null) {
385            throw new IllegalArgumentException
386                (eventName + " event does not provide the " + parameterDesc);
387        }
388       
389        return value;
390    }
391
392    /**
393     * <p>
394     * TODO: comment
395     * </p>
396     *
397     * @param eventName
398     * @param eventParameters
399     * @return
400     */
401    private int[] getCoordinateParameter(String       eventName,
402                                         List<String> eventParameters,
403                                         int          parameterIndex,
404                                         String       parameterDesc)
405    {
406        String value =
407            getStringParameter(eventName, eventParameters, parameterIndex, parameterDesc);
408       
409        String[] intStrs = value.split(",");
410       
411        if ((intStrs == null) || (intStrs.length != 2)) {
412            throw new IllegalArgumentException("the " + parameterDesc + " of an " + eventName +
413                                               " event does not provide two correct coordinates");
414        }
415       
416        try {
417            return new int[] { Integer.parseInt(intStrs[0]), Integer.parseInt(intStrs[1]) };
418        }
419        catch (NumberFormatException e) {
420            throw new IllegalArgumentException("the coordinates provided as " + parameterDesc +
421                                               " of an " + eventName + " event are no numbers");
422        }
423    }
424
425    /**
426     * <p>
427     * Checks whether an agent is a robot.
428     * </p>
429     *
430     * @param agent
431     *            agent that is checked
432     * @return true, if the agent is a robot; false otherwise
433     */
434    private boolean isRobot(String agent) {
435        return agent.matches(robotRegex);
436    }
437
438}
Note: See TracBrowser for help on using the repository browser.