Index: /trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/HTMLLogCompressor.java
===================================================================
--- /trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/HTMLLogCompressor.java	(revision 1230)
+++ /trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/HTMLLogCompressor.java	(revision 1230)
@@ -0,0 +1,350 @@
+//   Copyright 2012 Georg-August-Universität Göttingen, Germany
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+
+package de.ugoe.cs.autoquest.plugin.html;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.text.DecimalFormat;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.xml.sax.SAXException;
+
+import de.ugoe.cs.util.StringTools;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * TODO comment
+ * </p>
+ * 
+ * @author Fabian Glaser, Patrick Harms
+ * @version 1.0
+ * 
+ */
+public class HTMLLogCompressor extends AbstractDefaultLogParser {
+    
+    /**
+     * 
+     */
+    private PrintWriter outputWriter;
+    
+    /**
+     * the GUI elements, that were already logged and need therefore not be logged again into
+     * the same file
+     */
+    private Set<String> loggedGUIElementIds = new HashSet<String>();
+
+    /**
+     * the events that were read
+     */
+    private List<EventEntry> sortedEvents = new LinkedList<EventEntry>();
+
+    /**
+     * 
+     */
+    public void compressFilesInDirectory(String directory) {
+        if (directory == null) {
+            throw new IllegalArgumentException("directory must not be null");
+        }
+
+        compressFilesInDirectory(new File(directory));
+    }
+
+    /**
+     * 
+     */
+    public void compressFilesInDirectory(File directory) {
+        if (directory == null) {
+            throw new IllegalArgumentException("directory must not be null");
+        }
+        
+        if (!directory.exists()) {
+            throw new IllegalArgumentException("directory must denote an existing directory");
+        }
+        
+        if (!directory.isDirectory()) {
+            throw new IllegalArgumentException("directory must denote a directory");
+        }
+        
+        File[] files = directory.listFiles();
+        
+        if ((files == null) || (files.length == 0)) {
+            Console.println("directory is empty");
+            return;
+        }
+        
+        File outFile = null;
+        
+        int logFileIndex = -1;
+        do {
+            outFile = new File(directory, getLogFileName(directory.getName(), logFileIndex));
+            logFileIndex++;
+        }
+        while (outFile.exists());
+        
+        List<File> compressedFiles = new LinkedList<File>();
+        
+        try {
+            FileOutputStream fis = new FileOutputStream(outFile);
+            outputWriter = new PrintWriter(new OutputStreamWriter(fis, "UTF-8"));
+            outputWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+            outputWriter.println("<session>");
+
+            loggedGUIElementIds.clear();
+            sortedEvents.clear();
+
+            for (File file : files) {
+                if (file.isFile()) {
+                    try {
+                        super.parseFile(file);
+                        Console.println("compressed " + file);
+                        compressedFiles.add(file);
+                    }
+                    catch (Exception e) {
+                        Console.printerrln("could not parse and compress file " + file);
+                    }
+                }
+            }
+            
+            for (EventEntry event : sortedEvents) {
+                event.dump();
+            }
+
+            outputWriter.println("</session>");
+            outputWriter.flush();
+        }
+        catch (FileNotFoundException e) {
+            Console.printerrln("could not create compressed file " + outFile);
+            compressedFiles.clear();
+        }
+        catch (UnsupportedEncodingException e) {
+            // this should never happen
+            e.printStackTrace();
+            compressedFiles.clear();
+        }
+        finally {
+            if (outputWriter != null) {
+                outputWriter.close();
+                outputWriter = null;
+            }
+        }
+        
+        for (File file : compressedFiles) {
+            if (!file.delete()) {
+                Console.printerrln("could not delete compressed file " + file);
+            }
+        }
+        
+        if (outFile.exists()) {
+            File finalOutFile = null;
+            
+            logFileIndex = -1;
+            do {
+                logFileIndex++;
+                finalOutFile =
+                    new File(directory, getLogFileName(directory.getName(), logFileIndex));
+            }
+            while (finalOutFile.exists());
+
+            outFile.renameTo(finalOutFile);
+            Console.println("created " + finalOutFile);
+        }
+    }
+    
+    /* (non-Javadoc)
+     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#parseFile(java.lang.String)
+     */
+    @Override
+    public void parseFile(String filename) {
+        throw new IllegalStateException("this method must not be called externally");
+    }
+
+    /* (non-Javadoc)
+     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#parseFile(java.io.File)
+     */
+    @Override
+    public void parseFile(File file) {
+        throw new IllegalStateException("this method must not be called externally");
+    }
+
+    /* (non-Javadoc)
+     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#handleGUIElement(String, Map)
+     */
+    @Override
+    protected boolean handleGUIElement(String id, Map<String, String> parameters)
+        throws SAXException
+    {
+        if (!loggedGUIElementIds.contains(id)) {
+            outputWriter.print("<component id=\"");
+            outputWriter.print(id);
+            outputWriter.println("\">");
+        
+            for (Map.Entry<String, String> param : parameters.entrySet()) {
+                dumpParam(param.getKey(), param.getValue());
+            }
+            
+            outputWriter.println("</component>");
+        
+            loggedGUIElementIds.add(id);
+        }
+        
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * @see de.ugoe.cs.autoquest.plugin.html.AbstractDefaultLogParser#handleEvent(java.lang.String, java.util.Map)
+     */
+    @Override
+    protected boolean handleEvent(String type, Map<String, String> parameters) throws SAXException {
+        String timestampStr = parameters.get("timestamp");
+        
+        long timestamp = Long.MAX_VALUE;
+        
+        if (timestampStr != null) {
+            timestamp = Long.parseLong(timestampStr);
+        }
+        
+        EventEntry newEvent = new EventEntry(type, parameters, timestamp);
+        
+        boolean added = false;
+        for (int i = 0; i < sortedEvents.size(); i++) {
+            if (sortedEvents.get(i).timestamp > newEvent.timestamp) {
+                sortedEvents.add(i, newEvent);
+                added = true;
+                break;
+            }
+        }
+        
+        if (!added) {
+            sortedEvents.add(newEvent);
+        }
+
+        return true;
+    }
+
+    /**
+     * <p>
+     * dumps a parameter with the given name and value to the log file. The result is a
+     * tag named param with a name attribute and a value attribute. The value is transformed
+     * to a String if it is no String already. Furthermore, an XML entity replacement is performed
+     * if required.
+     * </p>
+     *
+     * @param name  the name of the parameter to be dumped
+     * @param value the value of the parameter to be dumped
+     */
+    private void dumpParam(String name, Object value) {
+        if (value == null) {
+            return;
+        }
+        
+        String val;
+        
+        if (value instanceof String) {
+            val = (String) value;
+        }
+        else {
+            val = String.valueOf(value);
+        }
+        
+        outputWriter.print(" <param name=\"");
+        outputWriter.print(name);
+        outputWriter.print("\" value=\"");
+        outputWriter.print(StringTools.xmlEntityReplacement(val));
+        outputWriter.println("\"/>");
+    }
+
+
+    /**
+     * <p>
+     * used to calculate a log file name. If the provided index is smaller 0, then no index
+     * is added to the file name. A filename is e.g. "htmlmonitor_12345_001.log". 
+     * </p>
+     *
+     * @param index the index of the log file or -1 one, if no index shall be added
+     * 
+     * @return the file name as described
+     */
+    private String getLogFileName(String clientId, int index) {
+        String result = "htmlmonitor_" + clientId;
+        
+        if (index >= 0) {
+            result += "_" + new DecimalFormat("000" ).format(index);
+        }
+        
+        result += ".log";
+        
+        return result;
+    }
+    
+    /**
+     * 
+     */
+    private class EventEntry {
+        
+        /**
+         * 
+         */
+        private String type;
+        
+        /**
+         * 
+         */
+        private Map<String, String> parameters;
+        
+        /**
+         * 
+         */
+        private long timestamp;
+
+        /**
+         * <p>
+         * TODO: comment
+         * </p>
+         *
+         * @param type
+         * @param parameters
+         * @param timestamp
+         */
+        private EventEntry(String type, Map<String, String> parameters, long timestamp) {
+            this.type = type;
+            this.parameters = parameters;
+            this.timestamp = timestamp;
+        }
+        
+        /**
+         * 
+         */
+        private void dump() {
+            outputWriter.print("<event type=\"");
+            outputWriter.print(type);
+            outputWriter.println("\">");
+            
+            for (Map.Entry<String, String> param : parameters.entrySet()) {
+                dumpParam(param.getKey(), param.getValue());
+            }
+            
+            outputWriter.println("</event>");
+        }
+    }
+}
Index: /trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/commands/CMDcompressHTMLLogFiles.java
===================================================================
--- /trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/commands/CMDcompressHTMLLogFiles.java	(revision 1230)
+++ /trunk/autoquest-plugin-html/src/main/java/de/ugoe/cs/autoquest/plugin/html/commands/CMDcompressHTMLLogFiles.java	(revision 1230)
@@ -0,0 +1,93 @@
+//   Copyright 2012 Georg-August-Universität Göttingen, Germany
+//
+//   Licensed under the Apache License, Version 2.0 (the "License");
+//   you may not use this file except in compliance with the License.
+//   You may obtain a copy of the License at
+//
+//       http://www.apache.org/licenses/LICENSE-2.0
+//
+//   Unless required by applicable law or agreed to in writing, software
+//   distributed under the License is distributed on an "AS IS" BASIS,
+//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//   See the License for the specific language governing permissions and
+//   limitations under the License.
+
+package de.ugoe.cs.autoquest.plugin.html.commands;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+import de.ugoe.cs.autoquest.plugin.html.HTMLLogCompressor;
+import de.ugoe.cs.util.console.Command;
+import de.ugoe.cs.util.console.Console;
+
+/**
+ * <p>
+ * TODO comment
+ * </p>
+ * 
+ * @author Patrick Harms
+ * @version 1.0
+ */
+public class CMDcompressHTMLLogFiles implements Command {
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#run(java.util.List)
+     */
+    @Override
+    public void run(List<Object> parameters) {
+        String path;
+
+        try {
+            path = (String) parameters.get(0);
+        }
+        catch (Exception e) {
+            throw new IllegalArgumentException("illegal parameters provided: " + e);
+        }
+
+        File directory = new File(path);
+        if (!directory.isDirectory()) {
+            Console.printerrln(path + " is not a directory");
+            return;
+        }
+
+        compressDirectory(directory);
+
+    }
+
+    /**
+     * 
+     */
+    private void compressDirectory(File directory) {
+        if (directory.isDirectory()) {
+            File[] children = directory.listFiles();
+            Arrays.sort(children);
+            
+            boolean containsAtLeastOneFile = false;
+            for (File child : children) {
+                compressDirectory(child);
+                
+                containsAtLeastOneFile |= child.isFile();
+            }
+            
+            if (containsAtLeastOneFile) {
+                HTMLLogCompressor compressor = new HTMLLogCompressor();
+                compressor.compressFilesInDirectory(directory);
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see de.ugoe.cs.util.console.Command#help()
+     */
+    @Override
+    public String help() {
+        return "compressHTMLLogFiles <directory>";
+    }
+
+}
