source: trunk/autoquest-plugin-jfc/src/main/java/de/ugoe/cs/autoquest/plugin/jfc/JFCTraceCorrector.java @ 1079

Last change on this file since 1079 was 1079, checked in by pharms, 11 years ago
  • prevented findbugs warnings
File size: 40.9 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.jfc;
16
17import java.io.BufferedOutputStream;
18import java.io.File;
19import java.io.FileInputStream;
20import java.io.FileNotFoundException;
21import java.io.FileOutputStream;
22import java.io.IOException;
23import java.io.InputStreamReader;
24import java.io.PrintStream;
25import java.io.UnsupportedEncodingException;
26import java.util.ArrayList;
27import java.util.Collections;
28import java.util.HashMap;
29import java.util.List;
30import java.util.Map;
31
32import javax.xml.parsers.ParserConfigurationException;
33import javax.xml.parsers.SAXParser;
34import javax.xml.parsers.SAXParserFactory;
35
36import org.xml.sax.Attributes;
37import org.xml.sax.InputSource;
38import org.xml.sax.SAXException;
39import org.xml.sax.SAXParseException;
40import org.xml.sax.helpers.DefaultHandler;
41
42import de.ugoe.cs.util.StringTools;
43import de.ugoe.cs.util.console.Console;
44
45/**
46 * <p>
47 * corrects older JFC log files which sometimes do not contain correct source specifications for
48 * events. It parses the file and adds component specifications to the sources, that do not have
49 * them. For each invalid source it checks, if there is another source with the same
50 * <code>toString</code> parameter but a complete list of components. If one is found, it is reused
51 * as source for the event with the wrong source. If none is found, a new component list is
52 * generated. This contains as parent components the components of the source of the previous event.
53 * The leaf component is parsed from the <code>toString</code> parameter that is provided with the
54 * source specifications. The resulting leaf nodes are not fully correct. They may pretend to
55 * be equal although they are not. Furthermore they may resist at a position in the GUI tree where
56 * they are not in reality. But more correctness is not achievable based on the
57 * <code>toString</code> parameter.
58 * </p>
59 *
60 * @version $Revision: $ $Date: 05.09.2012$
61 * @author 2012, last modified by $Author: pharms$
62 */
63public class JFCTraceCorrector  extends DefaultHandler {
64
65    /**
66     * <p>
67     * the file to write the result into
68     * </p>
69     */
70    private PrintStream outFile;
71   
72    /**
73     * <p>
74     * the currently parsed event
75     * </p>
76     */
77    private Event currentEvent;
78
79    /**
80     * <p>
81     * the currently parsed source of the currently parsed event
82     * </p>
83     */
84    private Source currentSource;
85
86    /**
87     * <p>
88     * the list of all sources parsed in a file identified through their <code>toString</code>
89     * representation
90     * </p>
91     */
92    private Map<String, List<Source>> allSources = new HashMap<String, List<Source>>();
93
94    /**
95     * <p>
96     * the currently parsed component of the currently parsed source of the currently parsed event
97     * </p>
98     */
99    private Component currentComponent;
100
101    /**
102     * <p>
103     * the currently parsed session
104     * </p>
105     */
106    private Session currentSession;
107
108    /**
109     * <p>
110     * corrects the given file and returns the name of the file into which the result was written
111     * </p>
112     *
113     * @param filename the name of the file to be corrected
114     *
115     * @return the name of the file with the corrected logfile
116     *
117     * @throws IllegalArgumentException if the filename is null
118     */
119    public String correctFile(String filename) throws IllegalArgumentException {
120        if (filename == null) {
121            throw new IllegalArgumentException("filename must not be null");
122        }
123
124        return correctFile(new File(filename)).getAbsolutePath();
125    }
126
127    /**
128     * <p>
129     * corrects the given file, stores the result in the second provided file and returns the
130     * name of the file into which the result was written
131     * </p>
132     *
133     * @param filename   the name of the file to be corrected
134     * @param resultFile the name of the file into which the corrected log shall be written
135     *
136     * @return the name of the file with the corrected logfile
137     *
138     * @throws IllegalArgumentException if the filename or resultFile is null
139     */
140    public String correctFile(String filename, String resultFile) throws IllegalArgumentException {
141        if ((filename == null) | (resultFile == null)) {
142            throw new IllegalArgumentException("filename and resultFile must not be null");
143        }
144
145        return correctFile(new File(filename), new File(resultFile)).getAbsolutePath();
146    }
147
148    /**
149     * <p>
150     * corrects the given file and returns the file into which the result was written. The name
151     * of the resulting file is contains the suffix "_corrected" before the dot.
152     * </p>
153     *
154     * @param file the file to be corrected
155     *
156     * @return the file containing the corrected logfile
157     *
158     * @throws IllegalArgumentException if the file is null
159     */
160    public File correctFile(File file) throws IllegalArgumentException {
161        if (file == null) {
162            throw new IllegalArgumentException("file must not be null");
163        }
164
165        int index = file.getName().lastIndexOf('.');
166        String fileName =
167            file.getName().substring(0, index) + "_corrected" + file.getName().substring(index);
168
169        File resultFile = new File(file.getParentFile(), fileName);
170
171        return correctFile(file, resultFile);
172    }
173
174    /**
175     * <p>
176     * corrects the given file, stores the result in the second provided file and returns the
177     * file into which the result was written
178     * </p>
179     *
180     * @param file       the file to be corrected
181     * @param resultFile the file into which the corrected log shall be written
182     *
183     * @return the file with the corrected logfile
184     *
185     * @throws IllegalArgumentException if the file or resultFile is null or if they are equal
186     */
187    public File correctFile(File file, File resultFile) throws IllegalArgumentException {
188        if ((file == null) || (resultFile == null)) {
189            throw new IllegalArgumentException("file and result file must not be null");
190        }
191       
192        if (file.getAbsolutePath().equals(resultFile.getAbsolutePath())) {
193            throw new IllegalArgumentException("file and result file must not be equal");
194        }
195       
196        try {
197            outFile = new PrintStream
198                (new BufferedOutputStream(new FileOutputStream(resultFile)), false, "UTF-8");
199            outFile.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
200        }
201        catch (FileNotFoundException e1) {
202            throw new IllegalArgumentException("could not create a corrected file name " +
203                                               resultFile + " next to " + file);
204        }
205        catch (UnsupportedEncodingException e) {
206            throw new IllegalArgumentException("this host does not support UTF-8 encoding");
207        }
208       
209
210        SAXParserFactory spf = SAXParserFactory.newInstance();
211        spf.setValidating(true);
212
213        SAXParser saxParser = null;
214        InputSource inputSource = null;
215        try {
216            saxParser = spf.newSAXParser();
217            inputSource =
218                new InputSource(new InputStreamReader(new FileInputStream(file), "UTF-8"));
219        }
220        catch (UnsupportedEncodingException e) {
221            Console.printerr("Error parsing file + " + file.getName());
222            Console.logException(e);
223            return null;
224        }
225        catch (ParserConfigurationException e) {
226            Console.printerr("Error parsing file + " + file.getName());
227            Console.logException(e);
228            return null;
229        }
230        catch (SAXException e) {
231            Console.printerr("Error parsing file + " + file.getName());
232            Console.logException(e);
233            return null;
234        }
235        catch (FileNotFoundException e) {
236            Console.printerr("Error parsing file + " + file.getName());
237            Console.logException(e);
238            return null;
239        }
240        if (inputSource != null) {
241            inputSource.setSystemId("file://" + file.getAbsolutePath());
242            try {
243                if (saxParser == null) {
244                    throw new RuntimeException("SAXParser creation failed");
245                }
246                saxParser.parse(inputSource, this);
247            }
248            catch (SAXParseException e) {
249                Console.printerrln("Failure parsing file in line " + e.getLineNumber() +
250                    ", column " + e.getColumnNumber() + ".");
251                Console.logException(e);
252                return null;
253            }
254            catch (SAXException e) {
255                Console.printerr("Error parsing file + " + file.getName());
256                Console.logException(e);
257                return null;
258            }
259            catch (IOException e) {
260                Console.printerr("Error parsing file + " + file.getName());
261                Console.logException(e);
262                return null;
263            }
264        }
265       
266        if (outFile != null) {
267            outFile.close();
268        }
269       
270        return resultFile;
271    }
272
273    /*
274     * (non-Javadoc)
275     *
276     * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String,
277     * java.lang.String, org.xml.sax.Attributes)
278     */
279    public void startElement(String uri, String localName, String qName, Attributes atts)
280        throws SAXException
281    {
282        if (qName.equals("sessions")) {
283            if (currentSession != null) {
284                throw new SAXException("nested sessions are not allowed");
285            }
286           
287            currentSession = new Session("sessions");
288        }
289        else if (qName.equals("newsession")) {
290            if (currentSession != null) {
291                currentSession.dump(outFile);
292            }
293           
294            currentSession = new Session("newsession");
295        }
296        else if (qName.equals("event")) {
297            if (currentEvent != null) {
298                throw new SAXException("nested events are not allowed");
299            }
300           
301            currentEvent = new Event(atts.getValue("id"));
302        }
303        else if (qName.equals("source")) {
304            if (currentSource != null) {
305                throw new SAXException("nested sources are not allowed");
306            }
307           
308            currentSource = new Source();
309        }
310        else if (qName.equals("component")) {
311            if (currentComponent != null) {
312                throw new SAXException("nested components are not allowed");
313            }
314           
315            currentComponent = new Component();
316        }
317        else if (qName.equals("param")) {
318            if (currentComponent != null) {
319                currentComponent.addParameter(atts.getValue("name"), atts.getValue("value"));
320            }
321            else if (currentSource != null) {
322                currentSource.addParameter(atts.getValue("name"), atts.getValue("value"));
323            }
324            else if (currentEvent != null) {
325                currentEvent.addParameter(atts.getValue("name"), atts.getValue("value"));
326            }
327            else {
328                throw new SAXException("parameter occurred at an unexpected place");
329            }
330        }
331        else {
332            throw new SAXException("unexpected tag " + qName);
333        }
334    }
335
336    /*
337     * (non-Javadoc)
338     *
339     * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String,
340     * java.lang.String)
341     */
342    @Override
343    public void endElement(String uri, String localName, String qName) throws SAXException {
344        // we do not have to check, if the current objects are null, as the Parser itself will
345        // throw an exception, if there is a closing tag for a non existent opening tag. But
346        // with a correct opening tag, the member variables will not be null (see startElement())
347        if (qName.equals("sessions")) {
348            correctSources(currentSession);
349
350            currentSession.dump(outFile);
351            currentSession = null;
352            allSources.clear();
353        }
354        else if (qName.equals("newsession")) {
355            correctSources(currentSession);
356           
357            currentSession.dump(outFile);
358            currentSession = null;
359            allSources.clear();
360        }
361        else if (qName.equals("event")) {
362            currentSession.addEvent(currentEvent);
363            currentEvent = null;
364        }
365        else if (qName.equals("source")) {
366            currentEvent.setSource(getUniqueSource(currentSource));
367            currentSource = null;
368        }
369        else if (qName.equals("component")) {
370            currentSource.addComponent(currentComponent);
371            currentComponent = null;
372        }
373        else if (!qName.equals("param")) {
374            throw new SAXException("unexpected closing tag " + qName);
375        }
376
377    }
378
379    /**
380     * <p>
381     * returns a source object, that is equal to the provided one but that is unique throughout
382     * the parsing process. The method may return the provided source, if this is the first
383     * occurrence of this source. This method is needed to reduce the amount of source
384     * representations that are instantiated during parsing log files.
385     * </p>
386     *
387     * @param source the source to search for a unique representation
388     *
389     * @return the unique representation of the source
390     */
391    private Source getUniqueSource(Source source) {
392        Source existingSource = null;
393       
394        List<Source> candidates = allSources.get(source.getToStringValue());
395       
396        if (candidates != null) {
397            for (Source candidate : candidates) {
398                if (candidate.equals(source)) {
399                    existingSource = candidate;
400                    break;
401                }
402            }
403        }
404       
405        if (existingSource == null) {
406            if (candidates == null) {
407                candidates = new ArrayList<Source>();
408                allSources.put(source.getToStringValue(), candidates);
409            }
410           
411            candidates.add(source);
412            existingSource = source;
413        }
414       
415        return existingSource;
416    }
417
418    /**
419     * <p>
420     * convenience method to find a source based on its <code>toString</code> parameter value.
421     * The method only returns sources, which match the provided <code>toString</code>
422     * representation and which have a valid list of components (size greater 0).
423     * </p>
424     *
425     * @param toStringValue the value of the <code>toString</code> parameter the source to find
426     *                      must have
427     *
428     * @return the source matching the parameter and having a valid list of components or null if
429     *         none is found
430     */
431    private Source findValidSource(String toStringValue) {
432        Source existingSource = null;
433       
434        List<Source> candidates = allSources.get(toStringValue);
435       
436        if (candidates != null) {
437            for (Source candidate : candidates) {
438                if ((candidate.getComponents() != null) && (candidate.getComponents().size() > 0))
439                {
440                    existingSource = candidate;
441                    break;
442                }
443            }
444        }
445       
446        return existingSource;
447    }
448
449    /**
450     * <p>
451     * corrects all wrong sources in the events of the session. For each wrong resource, the
452     * {@link #correctEventSource(Event, Source)} method is called.
453     * </p>
454     *
455     * @param session the session of which the events shall be corrected
456     */
457    private void correctSources(Session session) {
458        Source previousSource = null;
459        for (Event event : session.getEvents()) {
460            if ((event.getSource() == null) || (event.getSource().getComponents() == null) ||
461                (event.getSource().getComponents().size() == 0))
462            {
463                correctEventSource(event, previousSource);
464            }
465           
466            previousSource = event.getSource();
467        }
468    }
469
470    /**
471     * <p>
472     * corrects the source of an event. It first searches for a correct source with an equal
473     * <code>toString</code> parameter. If there is any, this is reused. Otherwise it creates a
474     * new correct source. For this, it copies all parameters of the source of the provided previous
475     * event if they are not included in the source already. Furthermore, it adds all components
476     * of the source of the previous event. At last, it adds a further component based on the
477     * information found in the <code>toString</code> parameter of the source.
478     * </p>
479     *
480     * @param event          the event of which the source must be corrected
481     * @param previousSource the source of the previous event to be potentially reused partially
482     */
483    private void correctEventSource(Event event, Source previousSource) {
484        Source existingSource = null;
485       
486        if ((event.getSource() != null) && (event.getSource().getToStringValue() != null)) {
487            existingSource = findValidSource(event.getSource().getToStringValue());
488        }
489       
490        if (existingSource != null) {
491            event.setSource(existingSource);
492        }
493        else {
494            if (previousSource != null) {
495                for (Parameter parameterOfPreviousSource : previousSource.getParameters()) {
496                    boolean foundParameter = false;
497                    for (Parameter parameter : event.getSource().getParameters()) {
498                        if (parameter.getName().equals(parameterOfPreviousSource.getName())) {
499                            foundParameter = true;
500                            break;
501                        }
502                    }
503
504                    if (!foundParameter) {
505                        event.getSource().addParameter(parameterOfPreviousSource);
506                    }
507                }
508   
509                for (Component component : previousSource.getComponents()) {
510                    if (!(component instanceof ComponentFromToString)) {
511                        event.getSource().addComponent(component);
512                    }
513                }
514            }
515
516            event.getSource().addComponent
517                (getComponentFromToString(event.getSource().getToStringValue()));
518        }
519    }
520
521    /**
522     * <p>
523     * determines a component based on the <code>toString</code> parameter of a source.
524     * For this, it parses the parameter value and tries to determine several infos such as the
525     * type and the name of it. The resulting components are not always distinguishable. This is,
526     * because the <code>toString</code> parameter does not contain sufficient information for
527     * correct identification.
528     * </p>
529     *
530     * @param toStringValue the <code>toString</code> parameter of a source
531     *
532     * @return the component parsed from the <code>toString</code> parameter
533     */
534    private Component getComponentFromToString(String toStringValue) {
535        ComponentFromToString component = new ComponentFromToString();
536       
537        // search for the beginning of the parameters section. Up to this position we find the class
538        int start = toStringValue.indexOf('[');
539        String clazz = toStringValue.substring(0, start);
540       
541        // the first parameters are x and y coordinate as well as the size. The size is one
542        // parameter, where with and height are separated with an 'x'
543        start = toStringValue.indexOf(',', start) + 1;
544        int end = toStringValue.indexOf(',', start);
545       
546        component.setX(Integer.parseInt(toStringValue.substring(start, end)));
547       
548        start = end + 1;
549        end = toStringValue.indexOf(',', start);
550
551        component.setY(Integer.parseInt(toStringValue.substring(start, end)));
552
553        start = end + 1;
554        end = toStringValue.indexOf('x', start);
555
556        component.setWidth(Integer.parseInt(toStringValue.substring(start, end)));
557
558        start = end + 1;
559        end = toStringValue.indexOf(',', start);
560
561        component.setHeight(Integer.parseInt(toStringValue.substring(start, end)));
562
563        // no start parsing the rest of the parameters and extract those having a key and a
564        // value and whose key is text, defaultIcon, or an alignment
565        int intermediate;
566        start = end + 1;
567
568        String title = null;
569        String icon = null;
570        String alignment = null;
571       
572        do {
573            end = toStringValue.indexOf(',', start);
574            intermediate = toStringValue.indexOf('[', start);
575           
576            if ((intermediate >= 0) && (intermediate < end)) {
577                // the value of the parameter itself contains brackets. So try to determine the
578                // real end of the parameter
579                end = toStringValue.indexOf(']', intermediate);
580                end = toStringValue.indexOf(',', end);
581            }
582           
583            if (end < 0) {
584                //we reached the end of the stream. So the the end to the "end"
585                end = toStringValue.lastIndexOf(']');
586            }
587           
588            intermediate = toStringValue.indexOf('=', start);
589           
590            if ((intermediate >= 0) && (intermediate < end)) {
591                // this is a key value pair, so store the the parameter
592                String key = toStringValue.substring(start, intermediate);
593                String value = toStringValue.substring(intermediate + 1, end);
594               
595                if ("text".equals(key)) {
596                    title = value;
597                }
598                else if ("defaultIcon".equals(key)) {
599                    icon = value;
600                }
601                else if ("alignmentX".equals(key) || "alignmentY".equals(key)) {
602                    if (alignment == null) {
603                        alignment = value;
604                    }
605                    else {
606                        alignment += "/" + value;
607                    }
608                }
609            }
610            /*else {
611                // this is a simple value, for now simply ignore it
612                String key = toStringValue.substring(start, end);
613                if (!"invalid".equals(key)) {
614                    componentHash += key.hashCode();
615                    component.params.add(new String[] { key, "true" });
616                }
617            }*/
618           
619            start = end + 1;
620        }
621        while (start < toStringValue.lastIndexOf(']'));
622       
623        // finish the component specification by setting the parameters
624        if ((title == null) || "".equals(title) || "null".equals(title)) {
625            if ((icon == null) || "".equals(icon) || "null".equals(icon)) {
626                title = clazz.substring(clazz.lastIndexOf('.') + 1) + "(";
627               
628                // to be able to distinguish some elements, that usually have no name and icon, try
629                // to include some of their specific identifying information in their name.
630                if ("org.tigris.gef.presentation.FigTextEditor".equals(clazz) ||
631                    "org.argouml.core.propertypanels.ui.UMLTextField".equals(clazz))
632                {
633                    title += "height " + component.height + ", ";
634                }
635                else if ("org.argouml.core.propertypanels.ui.UMLLinkedList".equals(clazz) ||
636                         "org.argouml.core.propertypanels.ui.LabelledComponent".equals(clazz))
637                {
638                    title += "position " + component.getX() + "/" + component.getY() + ", ";
639                }
640               
641                title += "alignment " + alignment + ")";
642            }
643            else {
644                // to be able to distinguish some elements, that usually have no name but an icon,
645                // try to include some of their specific identifying information in their name.
646                if ("org.tigris.toolbar.toolbutton.PopupToolBoxButton".equals(clazz))
647                {
648                    icon = icon.substring(0, icon.lastIndexOf('@'));
649                    title = clazz.substring(clazz.lastIndexOf('.') + 1) + "(position " +
650                        component.getX() + ")";
651                }
652                else {
653                    title = icon;
654                }
655            }
656        }
657       
658        component.addParameter("title", title);
659        component.addParameter("class", clazz);
660        component.addParameter("icon", ((icon == null) ? "" : icon));
661        component.addParameter("index", "-1");
662       
663        int hashCode = clazz.hashCode() + title.hashCode();
664       
665        if (hashCode < 0) {
666            hashCode = -hashCode;
667        }
668       
669        component.addParameter("hash", Integer.toString(hashCode, 16));
670
671        return component;
672    }   
673
674    /**
675     * <p>
676     * used to dump a list of parameters to the provided print stream
677     * </p>
678     */
679    private static void dumpParams(PrintStream out, List<Parameter> params, String indent) {
680        for (Parameter param : params) {
681            out.print(indent);
682            out.print("<param name=\"");
683            out.print(StringTools.xmlEntityReplacement(param.getName()));
684            out.print("\" value=\"");
685            out.print(StringTools.xmlEntityReplacement(param.getValue()));
686            out.println("\" />");
687        }
688       
689    }
690   
691    /**
692     * <p>
693     * check if two parameter lists are equal. Thea are equal if the contain the same parameters
694     * ignoring their order.
695     * </p>
696     *
697     * @param params1 the first parameter list to be compared
698     * @param params2 the second parameter list to be compared
699     *
700     * @return true if both lists contain the same parameters, false else.
701     */
702    private static boolean parametersEqual(List<Parameter> params1, List<Parameter> params2) {
703        if (params1 == null) {
704            return params2 == null;
705        }
706       
707        if (params2 == null) {
708            return false;
709        }
710       
711        if (params1.size() != params2.size()) {
712            return false;
713        }
714       
715        boolean found;
716        for (Parameter param1 : params1) {
717            found = false;
718           
719            for (Parameter param2 : params2) {
720                if (param1.equals(param2)) {
721                    found = true;
722                    break;
723                }
724            }
725           
726            if (!found) {
727                return false;
728            }
729        }
730       
731        return true;
732    }
733   
734   
735    /**
736     * <p>
737     * used to carry all events of a session and to dump it to the output file
738     * </p>
739     */
740    private static class Session {
741       
742        /** */
743        private String type;
744       
745        /** */
746        private List<Event> events = new ArrayList<Event>();
747       
748        /**
749         *
750         */
751        private Session(String type) {
752            if (type == null) {
753                throw new IllegalArgumentException("type must not be null");
754            }
755           
756            this.type = type;
757        }
758       
759        /**
760         *
761         */
762        private void addEvent(Event event) {
763            if (event == null) {
764                throw new IllegalArgumentException("event must not be null");
765            }
766           
767            events.add(event);
768        }
769       
770        /**
771         * @return the events
772         */
773        private List<Event> getEvents() {
774            return Collections.unmodifiableList(events);
775        }
776
777        /**
778         *
779         */
780        private void dump(PrintStream out) {
781            if (out == null) {
782                throw new IllegalArgumentException("out must not be null");
783            }
784           
785            out.print("<");
786            out.print(type);
787            out.println(">");
788
789            for (Event event : events) {
790                event.dump(out);
791            }
792           
793            out.print("</");
794            out.print(type);
795            out.println(">");
796        }
797    }
798
799    /**
800     * <p>
801     * used to carry all information about an event and to dump it to the output file
802     * </p>
803     */
804    private static class Event {
805
806        /** */
807        private String id;
808
809        /** */
810        private List<Parameter> params = new ArrayList<Parameter>();
811
812        /** */
813        private Source source;
814       
815        /**
816         *
817         */
818        private Event(String id) {
819            if (id == null) {
820                throw new IllegalArgumentException("id must not be null");
821            }
822           
823            this.id = id;
824        }
825       
826        /**
827         * @param source the source to set
828         */
829        private void setSource(Source source) {
830            this.source = source;
831        }
832
833        /**
834         * @return the source
835         */
836        private Source getSource() {
837            return source;
838        }
839
840        /**
841         *
842         */
843        private void addParameter(String name, String value) {
844            if (name == null) {
845                throw new IllegalArgumentException("name must not be null");
846            }
847           
848            if (value == null) {
849                throw new IllegalArgumentException("value must not be null");
850            }
851           
852            params.add(new Parameter(name, value));
853        }
854       
855        /**
856         *
857         */
858        private void dump(PrintStream out) {
859            if (out == null) {
860                throw new IllegalArgumentException("out must not be null");
861            }
862           
863            out.print("<event id=\"");
864            out.print(StringTools.xmlEntityReplacement(id));
865            out.println("\">");
866           
867            dumpParams(out, params, " ");
868            source.dump(out);
869           
870            out.println("</event>");
871        }
872    }
873
874    /**
875     * <p>
876     * used to carry all information about a source of an event and to dump it to the output file
877     * </p>
878     */
879    private static class Source {
880       
881        /** */
882        private List<Parameter> params = new ArrayList<Parameter>();
883       
884        /** */
885        private List<Component> components = new ArrayList<Component>();
886       
887        /** */
888        private String toStringValue = null;
889
890        /**
891         *
892         */
893        private String getToStringValue() {
894            if (toStringValue == null) {
895                for (Parameter param : params) {
896                    if (("toString".equals(param.getName())) &&
897                        (param.getValue() != null) && (!"".equals(param.getValue())))
898                    {
899                        toStringValue = param.getValue();
900                        break;
901                    }
902                }
903            }
904           
905            return toStringValue;
906        }
907
908        /**
909         *
910         */
911        private void addParameter(String name, String value) {
912            if (name == null) {
913                throw new IllegalArgumentException("name must not be null");
914            }
915           
916            if (value == null) {
917                throw new IllegalArgumentException("value must not be null");
918            }
919           
920            params.add(new Parameter(name, value));
921        }
922       
923        /**
924         *
925         */
926        private void addParameter(Parameter parameter) {
927            if (parameter == null) {
928                throw new IllegalArgumentException("parameter must not be null");
929            }
930           
931            params.add(parameter);
932        }
933       
934        /**
935         * @return the params
936         */
937        private List<Parameter> getParameters() {
938            return Collections.unmodifiableList(params);
939        }
940
941        /**
942         *
943         */
944        private void addComponent(Component component) {
945            if (component == null) {
946                throw new IllegalArgumentException("component must not be null");
947            }
948           
949            components.add(component);
950        }
951       
952        /**
953         * @return the components
954         */
955        private List<Component> getComponents() {
956            return Collections.unmodifiableList(components);
957        }
958
959        /**
960         *
961         */
962        private void dump(PrintStream out) {
963            if (out == null) {
964                throw new IllegalArgumentException("out must not be null");
965            }
966           
967            out.println(" <source>");
968           
969            dumpParams(out, params, "  ");
970           
971            for (Component component : components) {
972                component.dump(out);
973            }
974           
975            out.println(" </source>");
976        }
977       
978        /* (non-Javadoc)
979         * @see java.lang.Object#equals(java.lang.Object)
980         */
981        @Override
982        public boolean equals(Object obj) {
983            if (this == obj) {
984                return true;
985            }
986           
987            if (obj instanceof Source) {
988                Source other = (Source) obj;
989               
990                if (getToStringValue() != null ?
991                      !getToStringValue().equals(other.getToStringValue()) :
992                          other.getToStringValue() != null)
993                {
994                    return false;
995                }
996               
997                if (!parametersEqual(params, other.params)) {
998                    return false;
999                }
1000               
1001                if (components == null) {
1002                    return other.components == null;
1003                }
1004               
1005                if (other.components == null) {
1006                    return false;
1007                }
1008               
1009                if (components.size() != other.components.size()) {
1010                    return false;
1011                }
1012
1013                for (int i = 0; i < components.size(); i++) {
1014                    if (!components.get(i).equals(other.components.get(i))) {
1015                        return false;
1016                    }
1017                }
1018               
1019                return true;
1020            }
1021           
1022            return false;
1023        }
1024
1025        /* (non-Javadoc)
1026         * @see java.lang.Object#hashCode()
1027         */
1028        @Override
1029        public int hashCode() {
1030            String str = getToStringValue();
1031           
1032            if (str != null) {
1033                return str.hashCode();
1034            }
1035            else {
1036                // ensure that all incomplete sources provide the same hashcode
1037                return 0;
1038            }
1039        }
1040
1041    }
1042   
1043    /**
1044     * <p>
1045     * used to carry all information about a component of a source and to dump it to the output file
1046     * </p>
1047     */
1048    private static class Component {
1049       
1050        /** */
1051        private List<Parameter> params = new ArrayList<Parameter>();
1052       
1053        /**
1054         *
1055         */
1056        protected void addParameter(String name, String value) {
1057            if (name == null) {
1058                throw new IllegalArgumentException("name must not be null");
1059            }
1060           
1061            if (value == null) {
1062                throw new IllegalArgumentException("value must not be null");
1063            }
1064           
1065            params.add(new Parameter(name, value));
1066        }
1067       
1068        /**
1069         * @return the params
1070         */
1071        private List<Parameter> getParameters() {
1072            return Collections.unmodifiableList(params);
1073        }
1074
1075        /**
1076         *
1077         */
1078        protected void dump(PrintStream out) {
1079            if (out == null) {
1080                throw new IllegalArgumentException("out must not be null");
1081            }
1082           
1083            out.println("  <component>");
1084            dumpParams(out, params, "   ");
1085            out.println("  </component>");
1086        }
1087
1088        /**
1089         *
1090         */
1091        public boolean equals(Object obj) {
1092            if (this == obj) {
1093                return true;
1094            }
1095           
1096            if (obj instanceof Component) {
1097                return parametersEqual(params, ((Component) obj).params);
1098            }
1099            else {   
1100               return false;
1101            }
1102        }
1103
1104        /* (non-Javadoc)
1105         * @see java.lang.Object#hashCode()
1106         */
1107        @Override
1108        public int hashCode() {
1109            // all components with an equally sized parameter list can be equal. This does not
1110            // work, if not all component parameters are set yet. But we do not use components
1111            // in a hash map so we provide an easy implementation
1112            return params.size();
1113        }
1114    }
1115
1116    /**
1117     * <p>
1118     * represents a specific component, which was read from the toString parameter of a source
1119     * </p>
1120     */
1121    private static class ComponentFromToString extends Component {
1122       
1123        /** */
1124        private int x;
1125       
1126        /** */
1127        private int y;
1128       
1129        /** */
1130        private int width;
1131       
1132        /** */
1133        private int height;
1134       
1135        /**
1136         * @param x the x to set
1137         */
1138        private void setX(int x) {
1139            this.x = x;
1140        }
1141
1142        /**
1143         * @return the x
1144         */
1145        private int getX() {
1146            return x;
1147        }
1148
1149       /**
1150         * @param y the y to set
1151         */
1152        private void setY(int y) {
1153            this.y = y;
1154        }
1155
1156        /**
1157         * @return the y
1158         */
1159        private int getY() {
1160            return y;
1161        }
1162
1163        /**
1164         * @param width the width to set
1165         */
1166        private void setWidth(int width) {
1167            this.width = width;
1168        }
1169
1170        /**
1171         * @param height the height to set
1172         */
1173        private void setHeight(int height) {
1174            this.height = height;
1175        }
1176
1177        /**
1178         *
1179         */
1180        @Override
1181        protected void dump(PrintStream out) {
1182            if (out == null) {
1183                throw new IllegalArgumentException("out must not be null");
1184            }
1185           
1186            out.println("  <component>");
1187           
1188            out.print("   ");
1189            out.print("<param name=\"x\" value=\"");
1190            out.print(x);
1191            out.println("\" />");
1192
1193            out.print("   ");
1194            out.print("<param name=\"y\" value=\"");
1195            out.print(y);
1196            out.println("\" />");
1197
1198            out.print("   ");
1199            out.print("<param name=\"width\" value=\"");
1200            out.print(width);
1201            out.println("\" />");
1202
1203            out.print("   ");
1204            out.print("<param name=\"height\" value=\"");
1205            out.print(height);
1206            out.println("\" />");
1207
1208            dumpParams(out, super.getParameters(), "   ");
1209            out.println("  </component>");
1210        }
1211       
1212        /**
1213         *
1214         */
1215        public boolean equals(Object obj) {
1216            if (!super.equals(obj)) {
1217                return false;
1218            }
1219           
1220            if (obj instanceof ComponentFromToString) {
1221                ComponentFromToString other = (ComponentFromToString) obj;
1222                return (x == other.x) && (y == other.y) &&
1223                    (width == other.width) && (height == other.height);
1224            }
1225            else {   
1226               return false;
1227            }
1228        }
1229
1230        /* (non-Javadoc)
1231         * @see java.lang.Object#hashCode()
1232         */
1233        @Override
1234        public int hashCode() {
1235            return super.hashCode() + x + y + width + height;
1236        }
1237    }
1238
1239    /**
1240     * <p>
1241     * used to carry all information about a parameter being a key and a value
1242     * </p>
1243     */
1244    private static class Parameter {
1245       
1246        /** */
1247        private String name;
1248       
1249        /** */
1250        private String value;
1251       
1252        /**
1253         *
1254         */
1255        private Parameter(String name, String value) {
1256            if (name == null) {
1257                throw new IllegalArgumentException("name must not be null");
1258            }
1259           
1260            if (value == null) {
1261                throw new IllegalArgumentException("value must not be null");
1262            }
1263           
1264            this.name = name;
1265            this.value = value;
1266        }
1267
1268        /**
1269         * @return the name
1270         */
1271        private String getName() {
1272            return name;
1273        }
1274
1275        /**
1276         * @return the value
1277         */
1278        private String getValue() {
1279            return value;
1280        }
1281
1282        /* (non-Javadoc)
1283         * @see java.lang.Object#equals(java.lang.Object)
1284         */
1285        @Override
1286        public boolean equals(Object obj) {
1287            if (obj instanceof Parameter) {
1288                return
1289                    (name.equals(((Parameter) obj).name) && value.equals(((Parameter) obj).value));
1290            }
1291            else {
1292                return false;
1293            }
1294        }
1295
1296        /* (non-Javadoc)
1297         * @see java.lang.Object#hashCode()
1298         */
1299        @Override
1300        public int hashCode() {
1301            return name.hashCode() + value.hashCode();
1302        }
1303       
1304       
1305    }
1306       
1307}
Note: See TracBrowser for help on using the repository browser.