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

Last change on this file since 1276 was 1272, checked in by pharms, 11 years ago
  • removed find bugs warnings
File size: 11.8 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        int start = 0;
225        int end = sortedEvents.size();
226        int center = 0;
227        long centerTimestamp;
228       
229        while (start != end) {
230            center = start + ((end - start) / 2);
231           
232            if ((center != start) || (center != end)) {
233                centerTimestamp = sortedEvents.get(center).timestamp;
234           
235                if (centerTimestamp < newEvent.timestamp) {
236                    start = Math.max(center, start + 1);
237                }
238                else if (centerTimestamp > newEvent.timestamp) {
239                    end = Math.min(center, end - 1);
240                }
241                else {
242                    // add the event directly where the center is, as the timestamps of the center
243                    // and the new event are equal
244                    end = center;
245                    start = end;
246                    break;
247                }
248            }
249            else {
250                // add the event to the position denoted by the add index
251                break;
252            }
253        }
254       
255        sortedEvents.add(start, newEvent);
256       
257        if ("onchange".equals(type)) {
258            String targetId = parameters.get("target");
259       
260            if ((targetId != null) && textInputFieldIds.contains(targetId)) {
261                String value = parameters.get("selectedValue");
262               
263                if ((value != null) && !value.endsWith("==")) {
264                    try {
265                        MessageDigest md = MessageDigest.getInstance("SHA-512");
266                        md.update(value.getBytes("UTF-8"));
267                        value =  Base64.encodeBase64String(md.digest());
268                    }
269                    catch (UnsupportedEncodingException e) {
270                        throw new IllegalStateException("Java VM does not support this code");
271                    }
272                    catch (NoSuchAlgorithmException e) {
273                        throw new IllegalStateException("Java VM does not support this code");
274                    }
275                   
276                    parameters.put("selectedValue", value);
277                }
278            }
279        }
280
281        return true;
282    }
283
284    /**
285     * <p>
286     * dumps a parameter with the given name and value to the log file. The result is a
287     * tag named param with a name attribute and a value attribute. The value is transformed
288     * to a String if it is no String already. Furthermore, an XML entity replacement is performed
289     * if required.
290     * </p>
291     *
292     * @param name  the name of the parameter to be dumped
293     * @param value the value of the parameter to be dumped
294     */
295    private void dumpParam(String name, Object value) {
296        if (value == null) {
297            return;
298        }
299       
300        String val;
301       
302        if (value instanceof String) {
303            val = (String) value;
304        }
305        else {
306            val = String.valueOf(value);
307        }
308       
309        outputWriter.print(" <param name=\"");
310        outputWriter.print(name);
311        outputWriter.print("\" value=\"");
312        outputWriter.print(StringTools.xmlEntityReplacement(val));
313        outputWriter.println("\"/>");
314    }
315
316
317    /**
318     * <p>
319     * this class is used internally for storing events in a sorted list together with the
320     * timestamps, being the sort criteria.
321     * </p>
322     */
323    private class EventEntry {
324       
325        /**
326         * <p>
327         * the type of the event
328         * </p>
329         */
330        private String type;
331       
332        /**
333         * <p>
334         * the parameters of the event
335         * </p>
336         */
337        private Map<String, String> parameters;
338       
339        /**
340         * <p>
341         * the timestamp of the event
342         * </p>
343         */
344        private long timestamp;
345
346        /**
347         * <p>
348         * creates a new event entry with event type, parameters and the timestamp
349         * </p>
350         */
351        private EventEntry(String type, Map<String, String> parameters, long timestamp) {
352            this.type = type;
353            this.parameters = parameters;
354            this.timestamp = timestamp;
355        }
356       
357        /**
358         * <p>
359         * convenience method for dumping the event into the compressed log file
360         * </p>
361         */
362        private void dump() {
363            outputWriter.print("<event type=\"");
364            outputWriter.print(type);
365            outputWriter.println("\">");
366           
367            for (Map.Entry<String, String> param : parameters.entrySet()) {
368                dumpParam(param.getKey(), param.getValue());
369            }
370           
371            outputWriter.println("</event>");
372        }
373    }
374}
Note: See TracBrowser for help on using the repository browser.