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

Last change on this file since 950 was 950, checked in by pharms, 12 years ago
  • initial version of the HTML plugin, still some things open
File size: 13.6 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.eventcore.HTMLEvent;
47import de.ugoe.cs.autoquest.plugin.html.guimodel.HTMLGUIElementSpec;
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;
84   
85    /**
86     *
87     */
88    private GUIModel 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        guiModel = new GUIModel();
124       
125        BufferedReader reader = null;
126       
127        try {
128            reader =
129                new BufferedReader(new InputStreamReader(new FileInputStream(source), "UTF-8"));
130       
131            loadRobotRegex();
132
133            List<Event> sequence = new ArrayList<Event>();
134
135            int lineCounter = 0;
136            String line = reader.readLine();
137            while ((line != null) && (!"".equals(line))) {
138                lineCounter++;
139                String[] values = line.substring(1, line.length() - 1).split("\" \"");
140
141                String clientId = values[0];
142                long timestamp = Long.parseLong(values[1]);
143                String title = values[2];
144                String uriString = values[3];
145                String agent = values[4];
146                String eventName = values[5];
147                String htmlElementPath = values[6];
148
149                List<String> eventParameters = null;
150                if (values.length > 7) {
151                    eventParameters = new ArrayList<String>();
152
153                    for (int i = 7; i < values.length; i++) {
154                        eventParameters.add(values[i]);
155                    }
156                }
157               
158                if (isRobot(agent)) {
159                    // do not handle sessions of robots
160                    Console.println("ignoring robot session: " + agent);
161                    break;
162                }
163               
164               
165                try {
166                    URL url = new URL(uriString);
167                    IGUIElement guiElement = getGUIElement(url, title, htmlElementPath);
168                    IInteraction interaction =
169                        getInteraction(eventName, guiElement, eventParameters);
170                   
171                    if (interaction != null) {
172                        Event event =
173                            createEvent(clientId, interaction, guiElement, timestamp, agent);
174                        sequence.add(event);
175                    }
176                }
177                catch (MalformedURLException e) {
178                    Console.traceln(Level.FINE, "Ignored line " + lineCounter + ": " +
179                                    e.getMessage());
180                }
181               
182                line = reader.readLine();
183            }
184           
185            Console.traceln(Level.INFO, "read user sequence with " + sequence.size() +
186                            " events from " + source);
187           
188            sequences = new ArrayList<List<Event>>();
189            sequences.add(sequence);
190        }
191        catch (Exception e) {
192            Console.printerrln("could not parse file " + source + ": " + e);
193            e.printStackTrace();
194        }
195    }
196
197    /**
198     * <p>
199     * TODO: comment
200     * </p>
201     *
202     * @return
203     */
204    public Collection<List<Event>> getSequences() {
205        return sequences;
206    }
207
208    /**
209     * <p>
210     * TODO: comment
211     * </p>
212     *
213     * @return
214     */
215    public GUIModel getGuiModel() {
216        return guiModel;
217    }
218
219    /**
220     * <p>
221     * Reads {@link #ROBOTFILTERFILE} and creates a regular expression that
222     * matches all the robots defined in the file. The regular expression is
223     * stored in the field {@link #robotRegex}.
224     * </p>
225     *
226     * @throws IOException
227     *             thrown if there is a problem reading the robot filter
228     * @throws FileNotFoundException
229     *             thrown if the robot filter is not found
230     */
231    private void loadRobotRegex() throws IOException, FileNotFoundException {
232        String[] lines = FileTools.getLinesFromFile(ROBOTFILTERFILE);
233        StringBuilder regex = new StringBuilder();
234        for (int i = 0; i < lines.length; i++) {
235            regex.append("(.*" + lines[i] + ".*)");
236            if (i != lines.length - 1) {
237                regex.append('|');
238            }
239        }
240        robotRegex = regex.toString();
241    }
242
243    /**
244     * <p>
245     * TODO: comment
246     * </p>
247     *
248     * @param htmlElementPath
249     * @return
250     * @throws GUIModelException
251     * @throws 
252     */
253    private IGUIElement getGUIElement(URL url, String title, String htmlElementPath)
254        throws GUIModelException
255    {
256        String[] pathElements = htmlElementPath.split("/");
257        List<IGUIElementSpec> guiElementPath = new ArrayList<IGUIElementSpec>();
258       
259        for (String pathElement : pathElements) {
260            if ((pathElement != null) && (!"".equals(pathElement))) {           
261                Matcher matcher = htmlElementPattern.matcher(pathElement);
262                if (!matcher.matches()) {
263                    throw new IllegalArgumentException("could not parse HTML element " + pathElement);
264                }
265
266                String type = matcher.group(1);
267                String indexStr = matcher.group(3);
268                String id = matcher.group(4);
269
270                int index = -1;
271
272                if ((indexStr != null) && (!"".equals(indexStr))) {
273                    index = Integer.parseInt(indexStr);
274                }
275
276                guiElementPath.add(new HTMLGUIElementSpec(url, title, type, id, index));
277            }
278        }
279       
280        return guiModel.integratePath(guiElementPath, GUIElementFactory.getInstance());
281    }
282
283    /**
284     * <p>
285     * TODO: comment
286     * </p>
287     *
288     * @param eventName
289     * @param guiElement
290     * @param eventParameters
291     * @return
292     */
293    private IInteraction getInteraction(String       eventName,
294                                        IGUIElement  guiElement,
295                                        List<String> eventParameters)
296    {
297        IInteraction result = null;
298       
299        if ("onclick".equals(eventName)) {
300            int[] coordinates =
301                getCoordinateParameter(eventName, eventParameters, 0, "click coordinates");
302            result = new MouseClick
303                (MouseButtonInteraction.Button.LEFT, coordinates[0], coordinates[1]);
304        }
305        else if ("onfocus".equals(eventName)) {
306            result = new KeyboardFocusChange();
307        }
308        else if ("onscroll".equals(eventName)) {
309            int[] coordinates =
310                getCoordinateParameter(eventName, eventParameters, 0, "click coordinates");
311            result = new Scroll(coordinates[0], coordinates[1]);
312        }
313        else if ("onchange".equals(eventName)) {
314            String value = getStringParameter(eventName, eventParameters, 0, "selected value");
315           
316            if ((guiElement instanceof ITextArea) || (guiElement instanceof ITextField)) {
317                result = new TextInput(value, null);
318            }
319            else {
320                throw new IllegalArgumentException("can not handle onchange events on GUI " +
321                                                   "elements of type " + guiElement.getClass());
322            }
323        }
324        else if ("onunload".equals(eventName) || "onbeforeunload".equals(eventName) ||
325                 "onpagehide".equals(eventName))
326        {
327            Console.traceln(Level.FINE, "Ignored event name \"onunload\"");
328        }
329        else {
330            throw new IllegalArgumentException("unknown event name: \"" + eventName + "\"");
331        }
332       
333        return result;
334    }
335
336    /**
337     * <p>
338     * TODO: comment
339     * </p>
340     *
341     * @param clientId
342     * @param interaction
343     * @param guiElement
344     * @param timestamp
345     * @param agent
346     * @return
347     */
348    private Event createEvent(String       clientId,
349                              IInteraction interaction,
350                              IGUIElement  guiElement,
351                              long         timestamp,
352                              String       agent)
353    {
354        return new HTMLEvent(clientId, interaction, guiElement, timestamp, agent);
355    }
356
357    /**
358     * <p>
359     * TODO: comment
360     * </p>
361     *
362     * @param eventName
363     * @param eventParameters
364     * @param i
365     * @param string
366     * @return
367     */
368    private String getStringParameter(String       eventName,
369                                      List<String> eventParameters,
370                                      int          parameterIndex,
371                                      String       parameterDesc)
372        throws IllegalArgumentException
373    {
374        String value =
375            eventParameters.size() > parameterIndex ? eventParameters.get(parameterIndex) : null;
376       
377        if (value == null) {
378            throw new IllegalArgumentException
379                (eventName + " event does not provide the " + parameterDesc);
380        }
381       
382        return value;
383    }
384
385    /**
386     * <p>
387     * TODO: comment
388     * </p>
389     *
390     * @param eventName
391     * @param eventParameters
392     * @return
393     */
394    private int[] getCoordinateParameter(String       eventName,
395                                         List<String> eventParameters,
396                                         int          parameterIndex,
397                                         String       parameterDesc)
398    {
399        String value =
400            getStringParameter(eventName, eventParameters, parameterIndex, parameterDesc);
401       
402        String[] intStrs = value.split(",");
403       
404        if ((intStrs == null) || (intStrs.length != 2)) {
405            throw new IllegalArgumentException("the " + parameterDesc + " of an " + eventName +
406                                               " event does not provide two correct coordinates");
407        }
408       
409        try {
410            return new int[] { Integer.parseInt(intStrs[0]), Integer.parseInt(intStrs[1]) };
411        }
412        catch (NumberFormatException e) {
413            throw new IllegalArgumentException("the coordinates provided as " + parameterDesc +
414                                               " of an " + eventName + " event are no numbers");
415        }
416    }
417
418    /**
419     * <p>
420     * Checks whether an agent is a robot.
421     * </p>
422     *
423     * @param agent
424     *            agent that is checked
425     * @return true, if the agent is a robot; false otherwise
426     */
427    private boolean isRobot(String agent) {
428        return agent.matches(robotRegex);
429    }
430
431}
Note: See TracBrowser for help on using the repository browser.