Index: /trunk/quest-plugin-jfc/src/main/java/de/ugoe/cs/quest/plugin/jfc/JFCTraceCorrector.java
===================================================================
--- /trunk/quest-plugin-jfc/src/main/java/de/ugoe/cs/quest/plugin/jfc/JFCTraceCorrector.java	(revision 839)
+++ /trunk/quest-plugin-jfc/src/main/java/de/ugoe/cs/quest/plugin/jfc/JFCTraceCorrector.java	(revision 840)
@@ -11,4 +11,5 @@
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -262,6 +263,9 @@
     {
         if (qName.equals("sessions")) {
-            currentSession = new Session();
-            currentSession.type = "sessions";
+            if (currentSession != null) {
+                throw new SAXException("nested sessions are not allowed");
+            }
+            
+            currentSession = new Session("sessions");
         }
         else if (qName.equals("newsession")) {
@@ -270,29 +274,36 @@
             }
             
-            currentSession = new Session();
-            currentSession.type = "newsession";
+            currentSession = new Session("newsession");
         }
         else if (qName.equals("event")) {
-            currentEvent = new Event();
-            currentEvent.id = atts.getValue("id");
+            if (currentEvent != null) {
+                throw new SAXException("nested events are not allowed");
+            }
+            
+            currentEvent = new Event(atts.getValue("id"));
         }
         else if (qName.equals("source")) {
+            if (currentSource != null) {
+                throw new SAXException("nested sources are not allowed");
+            }
+            
             currentSource = new Source();
         }
         else if (qName.equals("component")) {
+            if (currentComponent != null) {
+                throw new SAXException("nested components are not allowed");
+            }
+            
             currentComponent = new Component();
         }
         else if (qName.equals("param")) {
             if (currentComponent != null) {
-                currentComponent.params.add
-                    (new String[] {atts.getValue("name"), atts.getValue("value") });
+                currentComponent.addParameter(atts.getValue("name"), atts.getValue("value"));
             }
             else if (currentSource != null) {
-                currentSource.params.add
-                    (new String[] {atts.getValue("name"), atts.getValue("value") });
+                currentSource.addParameter(atts.getValue("name"), atts.getValue("value"));
             }
             else if (currentEvent != null) {
-                currentEvent.params.add
-                    (new String[] {atts.getValue("name"), atts.getValue("value") });
+                currentEvent.addParameter(atts.getValue("name"), atts.getValue("value"));
             }
             else {
@@ -313,4 +324,7 @@
     @Override
     public void endElement(String uri, String localName, String qName) throws SAXException {
+        // we do not have to check, if the current objects are null, as the Parser itself will
+        // throw an exception, if there is a closing tag for a non existent opening tag. But
+        // with a correct opening tag, the member variables will not be null (see startElement())
         if (qName.equals("sessions")) {
             correctSources(currentSession);
@@ -328,13 +342,13 @@
         }
         else if (qName.equals("event")) {
-            currentSession.events.add(currentEvent);
+            currentSession.addEvent(currentEvent);
             currentEvent = null;
         }
         else if (qName.equals("source")) {
-            currentEvent.source = getUniqueSource(currentSource);
+            currentEvent.setSource(getUniqueSource(currentSource));
             currentSource = null;
         }
         else if (qName.equals("component")) {
-            currentSource.components.add(currentComponent);
+            currentSource.addComponent(currentComponent);
             currentComponent = null;
         }
@@ -347,23 +361,35 @@
     /**
      * <p>
-     * stores a parsed source for later correction or reuse
+     * returns a source object, that is equal to the provided one but that is unique throughout
+     * the parsing process. The method may return the provided source, if this is the first
+     * occurrence of this source. This method is needed to reduce the amount of source
+     * representations that are instantiated during parsing log files. 
      * </p>
      *
-     * @param source the source to store
+     * @param source the source to search for a unique representation
+     * 
+     * @return the unique representation of the source
      */
     private Source getUniqueSource(Source source) {
-        String toStringValue = getToStringParam(source);
-
-        Source existingSource = findSource(toStringValue);
+        Source existingSource = null;
+        
+        List<Source> candidates = allSources.get(source.getToStringValue());
+        
+        if (candidates != null) {
+            for (Source candidate : candidates) {
+                if (candidate.equals(source)) {
+                    existingSource = candidate;
+                    break;
+                }
+            }
+        }
         
         if (existingSource == null) {
-            List<Source> sources = allSources.get(toStringValue);
-        
-            if (sources == null) {
-                sources = new ArrayList<Source>();
-                allSources.put(toStringValue, sources);
-            }
-            
-            sources.add(source);
+            if (candidates == null) {
+                candidates = new ArrayList<Source>();
+                allSources.put(source.getToStringValue(), candidates);
+            }
+            
+            candidates.add(source);
             existingSource = source;
         }
@@ -376,5 +402,5 @@
      * convenience method to find a source based on its <code>toString</code> parameter value.
      * The method only returns sources, which match the provided <code>toString</code>
-     * representation and which have a valid list of components.
+     * representation and which have a valid list of components (size greater 0).
      * </p>
      *
@@ -385,5 +411,5 @@
      *         none is found
      */
-    private Source findSource(String toStringValue) {
+    private Source findValidSource(String toStringValue) {
         Source existingSource = null;
         
@@ -392,6 +418,5 @@
         if (candidates != null) {
             for (Source candidate : candidates) {
-                if (toStringValue.equals(getToStringParam(candidate)) &&
-                    (candidate.components != null) && (candidate.components.size() > 0))
+                if ((candidate.getComponents() != null) && (candidate.getComponents().size() > 0))
                 {
                     existingSource = candidate;
@@ -414,12 +439,12 @@
     private void correctSources(Session session) {
         Source previousSource = null;
-        for (Event event : session.events) {
-            if ((event.source == null) || (event.source.components == null) ||
-                (event.source.components.size() == 0))
+        for (Event event : session.getEvents()) {
+            if ((event.getSource() == null) || (event.getSource().getComponents() == null) ||
+                (event.getSource().getComponents().size() == 0))
             {
                 correctEventSource(event, previousSource);
             }
             
-            previousSource = event.source;
+            previousSource = event.getSource();
         }
     }
@@ -439,25 +464,19 @@
      */
     private void correctEventSource(Event event, Source previousSource) {
-        String toStringValue = null;
-        
-        if ((event.source != null) && (event.source.params != null)) {
-            toStringValue = getToStringParam(event.source);
-        }
-        
         Source existingSource = null;
         
-        if (toStringValue != null) {
-            existingSource = findSource(toStringValue);
+        if ((event.getSource() != null) && (event.getSource().getToStringValue() != null)) {
+            existingSource = findValidSource(event.getSource().getToStringValue());
         }
         
         if (existingSource != null) {
-            event.source = existingSource;
+            event.setSource(existingSource);
         }
         else {
             if (previousSource != null) {
-                for (String[] parameterOfPreviousSource : previousSource.params) {
+                for (Parameter parameterOfPreviousSource : previousSource.getParameters()) {
                     boolean foundParameter = false;
-                    for (String[] parameter : event.source.params) {
-                        if (parameter[0].equals(parameterOfPreviousSource[0])) {
+                    for (Parameter parameter : event.getSource().getParameters()) {
+                        if (parameter.getName().equals(parameterOfPreviousSource.getName())) {
                             foundParameter = true;
                             break;
@@ -466,38 +485,18 @@
 
                     if (!foundParameter) {
-                        event.source.params.add(parameterOfPreviousSource);
+                        event.getSource().addParameter(parameterOfPreviousSource);
                     }
                 }
     
-                for (Component component : previousSource.components) {
+                for (Component component : previousSource.getComponents()) {
                     if (!(component instanceof ComponentFromToString)) {
-                        event.source.components.add(component);
+                        event.getSource().addComponent(component);
                     }
                 }
             }
 
-            event.source.components.add(getComponentFromToString(toStringValue));
-        }
-    }
-
-    /**
-     * <p>
-     * retrieves the value of the <code>toString</code> parameter of the provided source.
-     * </p>
-     *
-     * @param source the source to read the parameter of
-     * @return the value of the parameter
-     */
-    private String getToStringParam(Source source) {
-        String value = null;
-        
-        for (String[] param : source.params) {
-            if (("toString".equals(param[0])) && (param[1] != null) && (!"".equals(param[1]))) {
-                value = param[1];
-                break;
-            }
-        }
-        
-        return value;
+            event.getSource().addComponent
+                (getComponentFromToString(event.getSource().getToStringValue()));
+        }
     }
 
@@ -527,20 +526,20 @@
         int end = toStringValue.indexOf(',', start);
         
-        component.x = Integer.parseInt(toStringValue.substring(start, end));
+        component.setX(Integer.parseInt(toStringValue.substring(start, end)));
         
         start = end + 1;
         end = toStringValue.indexOf(',', start);
 
-        component.y = Integer.parseInt(toStringValue.substring(start, end));
+        component.setY(Integer.parseInt(toStringValue.substring(start, end)));
 
         start = end + 1;
         end = toStringValue.indexOf('x', start);
 
-        component.width = Integer.parseInt(toStringValue.substring(start, end));
+        component.setWidth(Integer.parseInt(toStringValue.substring(start, end)));
 
         start = end + 1;
         end = toStringValue.indexOf(',', start);
 
-        component.height = Integer.parseInt(toStringValue.substring(start, end));
+        component.setHeight(Integer.parseInt(toStringValue.substring(start, end)));
 
         // no start parsing the rest of the parameters and extract those having a key and a 
@@ -619,5 +618,5 @@
                          "org.argouml.core.propertypanels.ui.LabelledComponent".equals(clazz))
                 {
-                    title += "position " + component.x + "/" + component.y + ", ";
+                    title += "position " + component.getX() + "/" + component.getY() + ", ";
                 }
                 
@@ -629,8 +628,8 @@
         }
         
-        component.params.add(new String[] { "title", title } );
-        component.params.add(new String[] { "class", clazz } );
-        component.params.add(new String[] { "icon", icon } );
-        component.params.add(new String[] { "index", "-1" });
+        component.addParameter("title", title);
+        component.addParameter("class", clazz);
+        component.addParameter("icon", ((icon == null) ? "" : icon));
+        component.addParameter("index", "-1");
         
         int hashCode = clazz.hashCode() + title.hashCode();
@@ -640,5 +639,5 @@
         }
         
-        component.params.add(new String[] { "hash", Integer.toString(hashCode, 16) });
+        component.addParameter("hash", Integer.toString(hashCode, 16));
 
         return component;
@@ -650,15 +649,59 @@
      * </p>
      */
-    private void dumpParams(PrintStream out, List<String[]> params, String indent) {
-        for (String[] param : params) {
+    private void dumpParams(PrintStream out, List<Parameter> params, String indent) {
+        for (Parameter param : params) {
             out.print(indent);
             out.print("<param name=\"");
-            out.print(StringTools.xmlEntityReplacement(param[0]));
+            out.print(StringTools.xmlEntityReplacement(param.getName()));
             out.print("\" value=\"");
-            out.print(StringTools.xmlEntityReplacement(param[1]));
+            out.print(StringTools.xmlEntityReplacement(param.getValue()));
             out.println("\" />");
         }
         
     }
+    
+    /**
+     * <p>
+     * check if two parameter lists are equal. Thea are equal if the contain the same parameters
+     * ignoring their order.
+     * </p>
+     *
+     * @param params1 the first parameter list to be compared
+     * @param params2 the second parameter list to be compared
+     * 
+     * @return true if both lists contain the same parameters, false else.
+     */
+    private boolean parametersEqual(List<Parameter> params1, List<Parameter> params2) {
+        if (params1 == null) {
+            return params2 == null;
+        }
+        
+        if (params2 == null) {
+            return false;
+        }
+        
+        if (params1.size() != params2.size()) {
+            return false;
+        }
+        
+        boolean found;
+        for (Parameter param1 : params1) {
+            found = false;
+            
+            for (Parameter param2 : params2) {
+                if (param1.equals(param2)) {
+                    found = true;
+                    break;
+                }
+            }
+            
+            if (!found) {
+                return false;
+            }
+        }
+        
+        return true;
+    }
+    
     
     /**
@@ -668,8 +711,48 @@
      */
     private class Session {
+        
+        /** */
         private String type;
+        
+        /** */
         private List<Event> events = new ArrayList<Event>();
         
-        public void dump(PrintStream out) {
+        /**
+         * 
+         */
+        private Session(String type) {
+            if (type == null) {
+                throw new IllegalArgumentException("type must not be null");
+            }
+            
+            this.type = type;
+        }
+        
+        /**
+         * 
+         */
+        private void addEvent(Event event) {
+            if (event == null) {
+                throw new IllegalArgumentException("event must not be null");
+            }
+            
+            events.add(event);
+        }
+        
+        /**
+         * @return the events
+         */
+        private List<Event> getEvents() {
+            return Collections.unmodifiableList(events);
+        }
+
+        /**
+         * 
+         */
+        private void dump(PrintStream out) {
+            if (out == null) {
+                throw new IllegalArgumentException("out must not be null");
+            }
+            
             out.print("<");
             out.print(type);
@@ -692,9 +775,62 @@
      */
     private class Event {
+
+        /** */
         private String id;
-        private List<String[]> params = new ArrayList<String[]>();
+
+        /** */
+        private List<Parameter> params = new ArrayList<Parameter>();
+
+        /** */
         private Source source;
         
+        /**
+         * 
+         */
+        private Event(String id) {
+            if (id == null) {
+                throw new IllegalArgumentException("id must not be null");
+            }
+            
+            this.id = id;
+        }
+        
+        /**
+         * @param source the source to set
+         */
+        private void setSource(Source source) {
+            this.source = source;
+        }
+
+        /**
+         * @return the source
+         */
+        private Source getSource() {
+            return source;
+        }
+
+        /**
+         * 
+         */
+        private void addParameter(String name, String value) {
+            if (name == null) {
+                throw new IllegalArgumentException("name must not be null");
+            }
+            
+            if (value == null) {
+                throw new IllegalArgumentException("value must not be null");
+            }
+            
+            params.add(new Parameter(name, value));
+        }
+        
+        /**
+         * 
+         */
         private void dump(PrintStream out) {
+            if (out == null) {
+                throw new IllegalArgumentException("out must not be null");
+            }
+            
             out.print("<event id=\"");
             out.print(StringTools.xmlEntityReplacement(id));
@@ -714,8 +850,91 @@
      */
     private class Source {
-        private List<String[]> params = new ArrayList<String[]>();
+        
+        /** */
+        private List<Parameter> params = new ArrayList<Parameter>();
+        
+        /** */
         private List<Component> components = new ArrayList<Component>();
-
+        
+        /** */
+        private String toStringValue = null;
+
+        /**
+         *
+         */
+        private String getToStringValue() {
+            if (toStringValue == null) {
+                for (Parameter param : params) {
+                    if (("toString".equals(param.getName())) &&
+                        (param.getValue() != null) && (!"".equals(param.getValue())))
+                    {
+                        toStringValue = param.getValue();
+                        break;
+                    }
+                }
+            }
+            
+            return toStringValue;
+        }
+
+        /**
+         * 
+         */
+        private void addParameter(String name, String value) {
+            if (name == null) {
+                throw new IllegalArgumentException("name must not be null");
+            }
+            
+            if (value == null) {
+                throw new IllegalArgumentException("value must not be null");
+            }
+            
+            params.add(new Parameter(name, value));
+        }
+        
+        /**
+         * 
+         */
+        private void addParameter(Parameter parameter) {
+            if (parameter == null) {
+                throw new IllegalArgumentException("parameter must not be null");
+            }
+            
+            params.add(parameter);
+        }
+        
+        /**
+         * @return the params
+         */
+        private List<Parameter> getParameters() {
+            return Collections.unmodifiableList(params);
+        }
+
+        /**
+         * 
+         */
+        private void addComponent(Component component) {
+            if (component == null) {
+                throw new IllegalArgumentException("component must not be null");
+            }
+            
+            components.add(component);
+        }
+        
+        /**
+         * @return the components
+         */
+        private List<Component> getComponents() {
+            return Collections.unmodifiableList(components);
+        }
+
+        /**
+         * 
+         */
         private void dump(PrintStream out) {
+            if (out == null) {
+                throw new IllegalArgumentException("out must not be null");
+            }
+            
             out.println(" <source>");
             
@@ -728,4 +947,68 @@
             out.println(" </source>");
         }
+        
+        /* (non-Javadoc)
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            
+            if (obj instanceof Source) {
+                Source other = (Source) obj;
+                
+                if ((getToStringValue() != other.getToStringValue()) ||
+                    ((getToStringValue() != null) &&
+                     (!getToStringValue().equals(other.getToStringValue()))))
+                {
+                    return false;
+                }
+                
+                if (!parametersEqual(params, other.params)) {
+                    return false;
+                }
+                
+                if (components == null) {
+                    return other.components == null;
+                }
+                
+                if (other.components == null) {
+                    return false;
+                }
+                
+                if (components.size() != other.components.size()) {
+                    return false;
+                }
+
+                for (int i = 0; i < components.size(); i++) {
+                    if (!components.get(i).equals(other.components.get(i))) {
+                        return false;
+                    }
+                }
+                
+                return true;
+            }
+            
+            return false;
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            String str = getToStringValue();
+            
+            if (str != null) {
+                return str.hashCode();
+            }
+            else {
+                // ensure that all incomplete sources provide the same hashcode
+                return 0;
+            }
+        }
+
     }
     
@@ -736,7 +1019,38 @@
      */
     private class Component {
-        protected List<String[]> params = new ArrayList<String[]>();
-        
+        
+        /** */
+        private List<Parameter> params = new ArrayList<Parameter>();
+        
+        /**
+         * 
+         */
+        protected void addParameter(String name, String value) {
+            if (name == null) {
+                throw new IllegalArgumentException("name must not be null");
+            }
+            
+            if (value == null) {
+                throw new IllegalArgumentException("value must not be null");
+            }
+            
+            params.add(new Parameter(name, value));
+        }
+        
+        /**
+         * @return the params
+         */
+        private List<Parameter> getParameters() {
+            return Collections.unmodifiableList(params);
+        }
+
+        /**
+         * 
+         */
         protected void dump(PrintStream out) {
+            if (out == null) {
+                throw new IllegalArgumentException("out must not be null");
+            }
+            
             out.println("  <component>");
             dumpParams(out, params, "   ");
@@ -744,29 +1058,29 @@
         }
 
-        public boolean equals(Object other) {
-            if (this == other) {
+        /**
+         * 
+         */
+        public boolean equals(Object obj) {
+            if (this == obj) {
                 return true;
             }
             
-            if (!(other instanceof Component)) {
-                return false;
-            }
-            
-            Component otherComp = (Component) other;
-            
-            boolean allParamsEqual = (params.size() == otherComp.params.size());
-            
-            if (allParamsEqual) {
-                for (int i = 0; i < params.size(); i++) {
-                    if (!params.get(i)[0].equals(otherComp.params.get(i)[0]) &&
-                        !params.get(i)[1].equals(otherComp.params.get(i)[1]))
-                    {
-                        allParamsEqual = false;
-                        break;
-                    }
-                }
-            }
-            
-            return allParamsEqual;
+            if (obj instanceof Component) {
+                return parametersEqual(params, ((Component) obj).params);
+            }
+            else {   
+               return false;
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            // all components with an equally sized parameter list can be equal. This does not
+            // work, if not all component parameters are set yet. But we do not use components
+            // in a hash map so we provide an easy implementation
+            return params.size();
         }
     }
@@ -778,11 +1092,68 @@
      */
     private class ComponentFromToString extends Component {
+        
+        /** */
         private int x;
+        
+        /** */
         private int y;
+        
+        /** */
         private int width;
+        
+        /** */
         private int height;
         
+        /**
+         * @param x the x to set
+         */
+        private void setX(int x) {
+            this.x = x;
+        }
+
+        /**
+         * @return the x
+         */
+        private int getX() {
+            return x;
+        }
+
+       /**
+         * @param y the y to set
+         */
+        private void setY(int y) {
+            this.y = y;
+        }
+
+        /**
+         * @return the y
+         */
+        private int getY() {
+            return y;
+        }
+
+        /**
+         * @param width the width to set
+         */
+        private void setWidth(int width) {
+            this.width = width;
+        }
+
+        /**
+         * @param height the height to set
+         */
+        private void setHeight(int height) {
+            this.height = height;
+        }
+
+        /**
+         * 
+         */
         @Override
         protected void dump(PrintStream out) {
+            if (out == null) {
+                throw new IllegalArgumentException("out must not be null");
+            }
+            
             out.println("  <component>");
             
@@ -807,40 +1178,102 @@
             out.println("\" />");
 
-            dumpParams(out, params, "   ");
+            dumpParams(out, super.getParameters(), "   ");
             out.println("  </component>");
         }
         
-        /*public boolean equals(Object other) {
-            if (this == other) {
-                return true;
-            }
-            
-            if (!(other instanceof ComponentFromToString)) {
+        /**
+         * 
+         */
+        public boolean equals(Object obj) {
+            if (!super.equals(obj)) {
                 return false;
             }
             
-            ComponentFromToString otherComp = (ComponentFromToString) other;
-            
-            // ignore the coordinates and the width as it may change over time
-            boolean allParamsEqual =
-                (height == otherComp.height) && (params.size() == otherComp.params.size());
-            
-            if (allParamsEqual) {
-                for (int i = 0; i < params.size(); i++) {
-                    if (!"x".equals(params.get(i)[0]) && !"y".equals(params.get(i)[0]) &&
-                        !"width".equals(params.get(i)[0]) && !"height".equals(params.get(i)[0]) &&
-                        !"index".equals(params.get(i)[0]) && !"hash".equals(params.get(i)[0]) &&
-                        !params.get(i)[0].equals(otherComp.params.get(i)[0]) &&
-                        !params.get(i)[1].equals(otherComp.params.get(i)[1]))
-                    {
-                        allParamsEqual = false;
-                        break;
-                    }
-                }
-            }
-            
-            return allParamsEqual;
-        }*/
-    }
-
+            if (obj instanceof ComponentFromToString) {
+                ComponentFromToString other = (ComponentFromToString) obj;
+                return (x == other.x) && (y == other.y) &&
+                    (width == other.width) && (height == other.height);
+            }
+            else {   
+               return false;
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            return super.hashCode() + x + y + width + height;
+        }
+    }
+
+    /**
+     * <p>
+     * used to carry all information about a parameter being a key and a value
+     * </p> 
+     */
+    private class Parameter {
+        
+        /** */
+        private String name;
+        
+        /** */
+        private String value;
+        
+        /**
+         * 
+         */
+        private Parameter(String name, String value) {
+            if (name == null) {
+                throw new IllegalArgumentException("name must not be null");
+            }
+            
+            if (value == null) {
+                throw new IllegalArgumentException("value must not be null");
+            }
+            
+            this.name = name;
+            this.value = value;
+        }
+
+        /**
+         * @return the name
+         */
+        private String getName() {
+            return name;
+        }
+
+        /**
+         * @return the value
+         */
+        private String getValue() {
+            return value;
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof Parameter) {
+                return
+                    (name.equals(((Parameter) obj).name) && value.equals(((Parameter) obj).value));
+            }
+            else {
+                return false;
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            return name.hashCode() + value.hashCode();
+        }
+        
+        
+    }
+        
 }
