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

Last change on this file since 1246 was 1246, checked in by pharms, 11 years ago
  • bugfix to prevent null pointer in the case an onchange event does not have a value
File size: 11.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.html;
16
17import java.io.File;
18import java.io.FileNotFoundException;
19import java.io.FileOutputStream;
20import java.io.OutputStreamWriter;
21import java.io.PrintWriter;
22import java.io.UnsupportedEncodingException;
23import java.security.MessageDigest;
24import java.security.NoSuchAlgorithmException;
25import java.util.HashSet;
26import java.util.LinkedList;
27import java.util.List;
28import java.util.Map;
29import java.util.Set;
30
31import org.apache.commons.codec.binary.Base64;
32import org.xml.sax.SAXException;
33
34import de.ugoe.cs.util.StringTools;
35import de.ugoe.cs.util.console.Console;
36
37/**
38 * <p>
39 * TODO comment
40 * </p>
41 *
42 * @author Patrick Harms
43 * @version 1.0
44 *
45 */
46public class HTMLLogTextInputPseudomizer extends AbstractDefaultLogParser {
47   
48    /**
49     * <p>
50     * The output writer into which the pseudomized variant of the log file is written
51     * </p>
52     */
53    private PrintWriter outputWriter;
54   
55    /**
56     * <p>
57     * The set of text input fields found in the GUI model
58     * </p>
59     */
60    private Set<String> textInputFieldIds = new HashSet<String>();
61   
62    /**
63     * <p>
64     * the events that were read
65     * </p>
66     */
67    private List<EventEntry> sortedEvents = new LinkedList<EventEntry>();
68
69    /**
70     * <p>
71     * called to pseudomize all text inputs in the given log file. The method reuses
72     * {@link #pseudomizeFile(File)}.
73     * </p>
74     *
75     * @param file the log file in which the text inputs must be pseudomized
76     */
77    public void pseudomizeFile(String file) {
78        if (file == null) {
79            throw new IllegalArgumentException("file must not be null");
80        }
81
82        pseudomizeFile(new File(file));
83    }
84
85    /**
86     * <p>
87     * called to pseudomize all text inputs in the given log file. The given file is read
88     * completely. All GUI elements are written to an output file as they are. All events are
89     * written to an output file as they are, as well, as long as they do not represent test inputs.
90     * If they are text input events, the entered text is replaced by its hash value for
91     * pseudomizing the text input. Finally, the original log file is deleted and replaced by
92     * the pseudomized variant. Log files, which are already pseudomized, stay untouched.
93     * </p>
94     *
95     * @param file the log file in which the text inputs must be pseudomized
96     */
97    public void pseudomizeFile(File file) {
98        if (file == null) {
99            throw new IllegalArgumentException("file must not be null");
100        }
101       
102        if (!file.exists()) {
103            throw new IllegalArgumentException("file must denote an existing file");
104        }
105       
106        if (!file.isFile()) {
107            throw new IllegalArgumentException("file must denote a file");
108        }
109       
110        File outFile = new File(file.getParentFile(), file.getName() + "_tmp");
111        boolean parsingFailed = false;
112       
113        try {
114            FileOutputStream fis = new FileOutputStream(outFile);
115            outputWriter = new PrintWriter(new OutputStreamWriter(fis, "UTF-8"));
116            outputWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
117            outputWriter.println("<session>");
118
119            textInputFieldIds.clear();
120            sortedEvents.clear();
121           
122            try {
123                super.parseFile(file);
124            }
125            catch (SAXException e) {
126                parsingFailed = true;
127            }
128           
129            for (EventEntry event : sortedEvents) {
130                event.dump();
131            }
132           
133            outputWriter.println("</session>");
134            outputWriter.flush();
135        }
136        catch (FileNotFoundException e) {
137            Console.printerrln("could not create pseudomized file " + outFile);
138        }
139        catch (UnsupportedEncodingException e) {
140            // this should never happen
141            e.printStackTrace();
142        }
143        finally {
144            if (outputWriter != null) {
145                outputWriter.close();
146                outputWriter = null;
147            }
148        }
149       
150        if (!parsingFailed && outFile.exists()) {
151            if (!file.delete()) {
152                Console.printerrln("could not delete pseudomized file " + file);
153            }
154            else if (!outFile.renameTo(file)) {
155                Console.printerrln
156                    ("could not rename pseudomized file to original file name " + file);
157            }           
158            else {
159                Console.println("pseudomized file " + file);
160            }
161        }
162        else {
163            if (!outFile.delete()) {
164                Console.printerrln("could not delete temporary file " + outFile);
165            }
166        }
167    }
168   
169    /* (non-Javadoc)
170     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#parseFile(java.lang.String)
171     */
172    @Override
173    public void parseFile(String filename) {
174        throw new IllegalStateException("this method must not be called externally");
175    }
176
177    /* (non-Javadoc)
178     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#parseFile(java.io.File)
179     */
180    @Override
181    public void parseFile(File file) {
182        throw new IllegalStateException("this method must not be called externally");
183    }
184
185    /* (non-Javadoc)
186     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#handleGUIElement(String, Map)
187     */
188    @Override
189    protected boolean handleGUIElement(String id, Map<String, String> parameters)
190        throws SAXException
191    {
192        outputWriter.print("<component id=\"");
193        outputWriter.print(id);
194        outputWriter.println("\">");
195       
196        for (Map.Entry<String, String> param : parameters.entrySet()) {
197            dumpParam(param.getKey(), param.getValue());
198           
199            if ("tagname".equals(param.getKey()) && "input_text".equals(param.getValue())) {
200                textInputFieldIds.add(id);
201            }
202        }
203           
204        outputWriter.println("</component>");
205       
206        return true;
207    }
208
209    /* (non-Javadoc)
210     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#handleEvent(String,Map)
211     */
212    @Override
213    protected boolean handleEvent(String type, Map<String, String> parameters) throws SAXException {
214        String timestampStr = parameters.get("timestamp");
215       
216        long timestamp = Long.MAX_VALUE;
217       
218        if (timestampStr != null) {
219            timestamp = Long.parseLong(timestampStr);
220        }
221       
222        EventEntry newEvent = new EventEntry(type, parameters, timestamp);
223       
224        boolean added = false;
225        for (int i = 0; i < sortedEvents.size(); i++) {
226            if (sortedEvents.get(i).timestamp > newEvent.timestamp) {
227                sortedEvents.add(i, newEvent);
228                added = true;
229                break;
230            }
231        }
232       
233        if (!added) {
234            sortedEvents.add(newEvent);
235        }
236       
237        if ("onchange".equals(type)) {
238            String targetId = parameters.get("target");
239       
240            if ((targetId != null) && textInputFieldIds.contains(targetId)) {
241                String value = parameters.get("selectedValue");
242               
243                if ((value != null) && !value.endsWith("==")) {
244                    try {
245                        MessageDigest md = MessageDigest.getInstance("SHA-512");
246                        md.update(value.getBytes("UTF-8"));
247                        value =  Base64.encodeBase64String(md.digest());
248                    }
249                    catch (UnsupportedEncodingException e) {
250                        throw new IllegalStateException("Java VM does not support this code");
251                    }
252                    catch (NoSuchAlgorithmException e) {
253                        throw new IllegalStateException("Java VM does not support this code");
254                    }
255                   
256                    parameters.put("selectedValue", value);
257                }
258            }
259        }
260
261        return true;
262    }
263
264    /**
265     * <p>
266     * dumps a parameter with the given name and value to the log file. The result is a
267     * tag named param with a name attribute and a value attribute. The value is transformed
268     * to a String if it is no String already. Furthermore, an XML entity replacement is performed
269     * if required.
270     * </p>
271     *
272     * @param name  the name of the parameter to be dumped
273     * @param value the value of the parameter to be dumped
274     */
275    private void dumpParam(String name, Object value) {
276        if (value == null) {
277            return;
278        }
279       
280        String val;
281       
282        if (value instanceof String) {
283            val = (String) value;
284        }
285        else {
286            val = String.valueOf(value);
287        }
288       
289        outputWriter.print(" <param name=\"");
290        outputWriter.print(name);
291        outputWriter.print("\" value=\"");
292        outputWriter.print(StringTools.xmlEntityReplacement(val));
293        outputWriter.println("\"/>");
294    }
295
296
297    /**
298     * <p>
299     * this class is used internally for storing events in a sorted list together with the
300     * timestamps, being the sort criteria.
301     * </p>
302     */
303    private class EventEntry {
304       
305        /**
306         * <p>
307         * the type of the event
308         * </p>
309         */
310        private String type;
311       
312        /**
313         * <p>
314         * the parameters of the event
315         * </p>
316         */
317        private Map<String, String> parameters;
318       
319        /**
320         * <p>
321         * the timestamp of the event
322         * </p>
323         */
324        private long timestamp;
325
326        /**
327         * <p>
328         * creates a new event entry with event type, parameters and the timestamp
329         * </p>
330         */
331        private EventEntry(String type, Map<String, String> parameters, long timestamp) {
332            this.type = type;
333            this.parameters = parameters;
334            this.timestamp = timestamp;
335        }
336       
337        /**
338         * <p>
339         * convenience method for dumping the event into the compressed log file
340         * </p>
341         */
342        private void dump() {
343            outputWriter.print("<event type=\"");
344            outputWriter.print(type);
345            outputWriter.println("\">");
346           
347            for (Map.Entry<String, String> param : parameters.entrySet()) {
348                dumpParam(param.getKey(), param.getValue());
349            }
350           
351            outputWriter.println("</event>");
352        }
353    }
354}
Note: See TracBrowser for help on using the repository browser.