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

Last change on this file since 787 was 787, checked in by pharms, 12 years ago
  • added command for correcting JFC traces that do not contain correct sources of events
File size: 27.7 KB
Line 
1package de.ugoe.cs.quest.plugin.jfc;
2
3import java.io.File;
4import java.io.FileInputStream;
5import java.io.FileNotFoundException;
6import java.io.FileOutputStream;
7import java.io.IOException;
8import java.io.InputStreamReader;
9import java.io.PrintStream;
10import java.io.UnsupportedEncodingException;
11import java.util.ArrayList;
12import java.util.List;
13
14import javax.xml.parsers.ParserConfigurationException;
15import javax.xml.parsers.SAXParser;
16import javax.xml.parsers.SAXParserFactory;
17
18import org.xml.sax.Attributes;
19import org.xml.sax.InputSource;
20import org.xml.sax.SAXException;
21import org.xml.sax.SAXParseException;
22import org.xml.sax.helpers.DefaultHandler;
23
24import de.ugoe.cs.util.console.Console;
25
26/**
27 * <p>
28 * corrects older JFC log files which sometimes do not contain correct source specification for
29 * events. It parses the file and adds component specifications to the source, that do not have
30 * them. The parent components are reused from the last GUI element the user worked with. The
31 * leaf component is parsed from the <code>toString</code> parameter that is provided with the
32 * source specifications. The resulting leaf nodes are not fully correct. They may pretend to
33 * be equal although they are not. But more correctness is not achievable based on the
34 * <code>toString</code> parameter.
35 * </p>
36 *
37 * @version $Revision: $ $Date: 05.09.2012$
38 * @author 2012, last modified by $Author: pharms$
39 */
40public class JFCTraceCorrector  extends DefaultHandler {
41
42    /**
43     * <p>
44     * the file to write the result into
45     * </p>
46     */
47    private PrintStream outFile;
48   
49    /**
50     * <p>
51     * the currently parsed event
52     * </p>
53     */
54    private Event currentEvent;
55
56    /**
57     * <p>
58     * the currently parsed source of the currently parsed event
59     * </p>
60     */
61    private Source currentSource;
62
63    /**
64     * <p>
65     * the source of the last parsed event
66     * </p>
67     */
68    private Source lastSource;
69
70    /**
71     * <p>
72     * the currently parsed component of the currently parsed source of the currently parsed event
73     * </p>
74     */
75    private Component currentComponent;
76
77    /**
78     * <p>
79     * the currently parsed session
80     * </p>
81     */
82    private Session currentSession;
83
84    /**
85     * <p>
86     * corrects the given file and returns the name of the file into which the result was written
87     * </p>
88     *
89     * @param filename the name of the file to be corrected
90     *
91     * @return the name of the file with the corrected logfile
92     *
93     * @throws IllegalArgumentException if the filename is null
94     */
95    public String correctFile(String filename) throws IllegalArgumentException {
96        if (filename == null) {
97            throw new IllegalArgumentException("filename must not be null");
98        }
99
100        return correctFile(new File(filename)).getAbsolutePath();
101    }
102
103    /**
104     * <p>
105     * corrects the given file, stores the result in the second provided file and returns the
106     * name of the file into which the result was written
107     * </p>
108     *
109     * @param filename   the name of the file to be corrected
110     * @param resultFile the name of the file into which the corrected log shall be written
111     *
112     * @return the name of the file with the corrected logfile
113     *
114     * @throws IllegalArgumentException if the filename or resultFile is null
115     */
116    public String correctFile(String filename, String resultFile) throws IllegalArgumentException {
117        if ((filename == null) | (resultFile == null)) {
118            throw new IllegalArgumentException("filename and resultFile must not be null");
119        }
120
121        return correctFile(new File(filename), new File(resultFile)).getAbsolutePath();
122    }
123
124    /**
125     * <p>
126     * corrects the given file and returns the file into which the result was written. The name
127     * of the resulting file is contains the suffix "_corrected" before the dot.
128     * </p>
129     *
130     * @param file the file to be corrected
131     *
132     * @return the file containing the corrected logfile
133     *
134     * @throws IllegalArgumentException if the file is null
135     */
136    public File correctFile(File file) throws IllegalArgumentException {
137        if (file == null) {
138            throw new IllegalArgumentException("file must not be null");
139        }
140
141        int index = file.getName().lastIndexOf('.');
142        String fileName =
143            file.getName().substring(0, index) + "_corrected" + file.getName().substring(index);
144
145        File resultFile = new File(file.getParentFile(), fileName);
146
147        return correctFile(file, resultFile);
148    }
149
150    /**
151     * <p>
152     * corrects the given file, stores the result in the second provided file and returns the
153     * file into which the result was written
154     * </p>
155     *
156     * @param file       the file to be corrected
157     * @param resultFile the file into which the corrected log shall be written
158     *
159     * @return the file with the corrected logfile
160     *
161     * @throws IllegalArgumentException if the file or resultFile is null or if they are equal
162     */
163    public File correctFile(File file, File resultFile) throws IllegalArgumentException {
164        if ((file == null) || (resultFile == null)) {
165            throw new IllegalArgumentException("file and result file must not be null");
166        }
167       
168        if (file.getAbsolutePath().equals(resultFile.getAbsolutePath())) {
169            throw new IllegalArgumentException("file and result file must not be equal");
170        }
171       
172        try {
173            outFile = new PrintStream(new FileOutputStream(resultFile));
174            outFile.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
175        }
176        catch (FileNotFoundException e1) {
177            throw new IllegalArgumentException("could not create a corrected file name " +
178                                               resultFile + " next to " + file);
179        }
180       
181
182        SAXParserFactory spf = SAXParserFactory.newInstance();
183        spf.setValidating(true);
184
185        SAXParser saxParser = null;
186        InputSource inputSource = null;
187        try {
188            saxParser = spf.newSAXParser();
189            inputSource =
190                new InputSource(new InputStreamReader(new FileInputStream(file), "UTF-8"));
191        }
192        catch (UnsupportedEncodingException e) {
193            Console.printerr("Error parsing file + " + file.getName());
194            Console.logException(e);
195            return null;
196        }
197        catch (ParserConfigurationException e) {
198            Console.printerr("Error parsing file + " + file.getName());
199            Console.logException(e);
200            return null;
201        }
202        catch (SAXException e) {
203            Console.printerr("Error parsing file + " + file.getName());
204            Console.logException(e);
205            return null;
206        }
207        catch (FileNotFoundException e) {
208            Console.printerr("Error parsing file + " + file.getName());
209            Console.logException(e);
210            return null;
211        }
212        if (inputSource != null) {
213            inputSource.setSystemId("file://" + file.getAbsolutePath());
214            try {
215                if (saxParser == null) {
216                    throw new RuntimeException("SAXParser creation failed");
217                }
218                saxParser.parse(inputSource, this);
219            }
220            catch (SAXParseException e) {
221                Console.printerrln("Failure parsing file in line " + e.getLineNumber() +
222                    ", column " + e.getColumnNumber() + ".");
223                Console.logException(e);
224                return null;
225            }
226            catch (SAXException e) {
227                Console.printerr("Error parsing file + " + file.getName());
228                Console.logException(e);
229                return null;
230            }
231            catch (IOException e) {
232                Console.printerr("Error parsing file + " + file.getName());
233                Console.logException(e);
234                return null;
235            }
236        }
237       
238        if (outFile != null) {
239            outFile.close();
240        }
241       
242        return resultFile;
243    }
244
245    /*
246     * (non-Javadoc)
247     *
248     * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String,
249     * java.lang.String, org.xml.sax.Attributes)
250     */
251    public void startElement(String uri, String localName, String qName, Attributes atts)
252        throws SAXException
253    {
254        if (qName.equals("sessions")) {
255            currentSession = new Session();
256            currentSession.type = "sessions";
257        }
258        else if (qName.equals("newsession")) {
259            if (currentSession != null) {
260                currentSession.dump(outFile);
261            }
262           
263            currentSession = new Session();
264            currentSession.type = "newsession";
265        }
266        else if (qName.equals("event")) {
267            currentEvent = new Event();
268            currentEvent.id = atts.getValue("id");
269        }
270        else if (qName.equals("source")) {
271            currentSource = new Source();
272        }
273        else if (qName.equals("component")) {
274            currentComponent = new Component();
275        }
276        else if (qName.equals("param")) {
277            if (currentComponent != null) {
278                currentComponent.params.add
279                    (new String[] {atts.getValue("name"), atts.getValue("value") });
280            }
281            else if (currentSource != null) {
282                currentSource.params.add
283                    (new String[] {atts.getValue("name"), atts.getValue("value") });
284            }
285            else if (currentEvent != null) {
286                currentEvent.params.add
287                    (new String[] {atts.getValue("name"), atts.getValue("value") });
288            }
289            else {
290                throw new SAXException("parameter occurred at an unexpected place");
291            }
292        }
293        else {
294            throw new SAXException("unexpected tag " + qName);
295        }
296    }
297
298    /*
299     * (non-Javadoc)
300     *
301     * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String,
302     * java.lang.String)
303     */
304    @Override
305    public void endElement(String uri, String localName, String qName) throws SAXException {
306        if (qName.equals("sessions")) {
307            //harmonizeToStringComponents();
308
309            currentSession.dump(outFile);
310            currentSession = null;
311        }
312        else if (qName.equals("newsession")) {
313            //harmonizeToStringComponents();
314           
315            currentSession.dump(outFile);
316            currentSession = null;
317        }
318        else if (qName.equals("event")) {
319            currentSession.events.add(currentEvent);
320            currentEvent = null;
321        }
322        else if (qName.equals("source")) {
323            if (currentSource.components.size() == 0) {
324                correctEventSource(currentSource);
325            }
326           
327            lastSource = currentSource;
328            currentEvent.source = currentSource;
329            currentSource = null;
330        }
331        else if (qName.equals("component")) {
332            currentSource.components.add(currentComponent);
333            currentComponent = null;
334        }
335        else if (!qName.equals("param")) {
336            throw new SAXException("unexpected closing tag " + qName);
337        }
338
339    }
340
341    /**
342     *
343     */
344    /*private void harmonizeToStringComponents() {
345        List<SourceGroup> groups = determineSourceGroups();
346       
347        for (SourceGroup group : groups) {
348            group.dump(System.out);
349        }
350    }*/
351
352    /**
353     *
354     */
355    /*private List<SourceGroup> determineSourceGroups() {
356        List<SourceGroup> groups = new ArrayList<SourceGroup>();
357       
358        for (Event event : currentSession.events) {
359            Source source = event.source;
360            Component lastComponent = source.components.get(source.components.size() - 1);
361            if (lastComponent instanceof ComponentFromToString) {
362                SourceGroup group = getSourceGroup(groups, source);
363                List<ComponentFromToString> sourcesWithSameXCoordinate =
364                    group.leafComponents.get(((ComponentFromToString) lastComponent).x);
365               
366                if (sourcesWithSameXCoordinate == null) {
367                    sourcesWithSameXCoordinate = new ArrayList<ComponentFromToString>();
368                    group.leafComponents.put
369                        (((ComponentFromToString) lastComponent).x, sourcesWithSameXCoordinate);
370                }
371               
372                if (!sourcesWithSameXCoordinate.contains(lastComponent)) {
373                    sourcesWithSameXCoordinate.add(((ComponentFromToString) lastComponent));
374                }
375            }
376        }
377       
378        return groups;
379    }*/
380
381    /**
382     *
383     */
384    /*private SourceGroup getSourceGroup(List<SourceGroup> groups, Source source) {
385        SourceGroup resultingGroup = null;
386       
387        for (SourceGroup candidate : groups) {
388            if (candidate.parentComponents.size() == (source.components.size() - 1)) {
389                boolean allComponentsMatch = true;
390           
391                for (int i = 0; i < candidate.parentComponents.size(); i++) {
392                    if (!candidate.parentComponents.get(i).equals(source.components.get(i))) {
393                        allComponentsMatch = false;
394                        break;
395                    }
396                }
397               
398                if (allComponentsMatch) {
399                    resultingGroup = candidate;
400                    break;
401                }
402            }
403        }
404       
405        if (resultingGroup == null) {
406            resultingGroup = new SourceGroup();
407           
408            for (int i = 0; i < (source.components.size() - 1); i++) {
409                resultingGroup.parentComponents.add(source.components.get(i));
410            }
411           
412            groups.add(resultingGroup);
413        }
414       
415        return resultingGroup;
416    }*/
417
418    /**
419     * <p>
420     * corrects the source of an event. It copies all parameters of the source of the previous
421     * event if they are not included in the source already. Furthermore, it adds all components
422     * of the source of the previous event. At last, it adds a further component based on the
423     * information found in the <code>toString</code> parameter of the source.
424     * </p>
425     *
426     * @param source the source to be corrected
427     */
428    private void correctEventSource(Source source) {
429        for (String[] parameterOfLastSource : lastSource.params) {
430            boolean foundParameter = false;
431            for (String[] parameter : source.params) {
432                if (parameter[0].equals(parameterOfLastSource[0])) {
433                    foundParameter = true;
434                    break;
435                }
436            }
437           
438            if (!foundParameter) {
439                source.params.add(parameterOfLastSource);
440            }
441        }
442       
443        for (Component component : lastSource.components) {
444            if (!(component instanceof ComponentFromToString)) {
445                source.components.add(component);
446            }
447        }
448       
449        source.components.add(getComponentFromToString(source));
450    }
451
452    /**
453     * <p>
454     * determines a component based on the <code>toString</code> parameter of the provided source.
455     * For this, it parses the parameter value and tries to determine several infos such as the
456     * type and the name of it. The resulting components are not always distinguishable. This is,
457     * because the <code>toString</code> parameter does not contain sufficient information for
458     * correct identification.
459     * </p>
460     *
461     * @param source the source to extract the <code>toString</code> parameter from
462     *
463     * @return the component parsed from the <code>toString</code> parameter
464     */
465    private Component getComponentFromToString(Source source) {
466        String toStringValue = null;
467       
468        for (String[] parameter : source.params) {
469            if ("toString".equals(parameter[0])) {
470                toStringValue = parameter[1];
471                break;
472            }
473        }
474       
475        ComponentFromToString component = new ComponentFromToString();
476       
477        // search for the beginning of the parameters section. Up to this position we find the class
478        int start = toStringValue.indexOf('[');
479        String clazz = toStringValue.substring(0, start);
480       
481        // the first parameters are x and y coordinate as well as the size. The size is one
482        // parameter, where with and height are separated with an 'x'
483        start = toStringValue.indexOf(',', start) + 1;
484        int end = toStringValue.indexOf(',', start);
485       
486        component.x = Integer.parseInt(toStringValue.substring(start, end));
487       
488        start = end + 1;
489        end = toStringValue.indexOf(',', start);
490
491        component.y = Integer.parseInt(toStringValue.substring(start, end));
492
493        start = end + 1;
494        end = toStringValue.indexOf('x', start);
495
496        component.width = Integer.parseInt(toStringValue.substring(start, end));
497
498        start = end + 1;
499        end = toStringValue.indexOf(',', start);
500
501        component.height = Integer.parseInt(toStringValue.substring(start, end));
502
503        // no start parsing the rest of the parameters and extract those having a key and a
504        // value and whose key is text, defaultIcon, or an alignment
505        int intermediate;
506        start = end + 1;
507
508        String title = null;
509        String icon = null;
510        String alignment = null;
511       
512        do {
513            end = toStringValue.indexOf(',', start);
514            intermediate = toStringValue.indexOf('[', start);
515           
516            if ((intermediate >= 0) && (intermediate < end)) {
517                // the value of the parameter itself contains brackets. So try to determine the
518                // real end of the parameter
519                end = toStringValue.indexOf(']', intermediate);
520                end = toStringValue.indexOf(',', end);
521            }
522           
523            if (end < 0) {
524                //we reached the end of the stream. So the the end to the "end"
525                end = toStringValue.lastIndexOf(']');
526            }
527           
528            intermediate = toStringValue.indexOf('=', start);
529           
530            if ((intermediate >= 0) && (intermediate < end)) {
531                // this is a key value pair, so store the the parameter
532                String key = toStringValue.substring(start, intermediate);
533                String value = toStringValue.substring(intermediate + 1, end);
534               
535                if ("text".equals(key)) {
536                    title = value;
537                }
538                else if ("defaultIcon".equals(key)) {
539                    icon = value;
540                }
541                else if ("alignmentX".equals(key) || "alignmentY".equals(key)) {
542                    if (alignment == null) {
543                        alignment = value;
544                    }
545                    else {
546                        alignment += "/" + value;
547                    }
548                }
549            }
550            /*else {
551                // this is a simple value, for now simply ignore it
552                String key = toStringValue.substring(start, end);
553                if (!"invalid".equals(key)) {
554                    componentHash += key.hashCode();
555                    component.params.add(new String[] { key, "true" });
556                }
557            }*/
558           
559            start = end + 1;
560        }
561        while (start < toStringValue.lastIndexOf(']'));
562       
563        // finish the component specification by setting the parameters
564        if (title == null) {
565            if (icon == null) {
566                title = clazz.substring(clazz.lastIndexOf('.') + 1) + "(";
567               
568                // to be able to distinguish some elements, that usually have no name and icon, try
569                // to include some of their specific identifying information in their name.
570                if ("org.tigris.gef.presentation.FigTextEditor".equals(clazz) ||
571                    "org.argouml.core.propertypanels.ui.UMLTextField".equals(clazz))
572                {
573                    title += "height " + component.height + ", ";
574                }
575                else if ("org.argouml.core.propertypanels.ui.UMLLinkedList".equals(clazz) ||
576                         "org.argouml.core.propertypanels.ui.LabelledComponent".equals(clazz))
577                {
578                    title += "position " + component.x + "/" + component.y + ", ";
579                }
580               
581                title += "alignment " + alignment + ")";
582            }
583            else {
584                title = icon;
585            }
586        }
587       
588        component.params.add(new String[] { "title", title } );
589        component.params.add(new String[] { "class", clazz } );
590        component.params.add(new String[] { "icon", icon } );
591        component.params.add(new String[] { "index", "-1" });
592       
593        int hashCode = clazz.hashCode() + title.hashCode();
594       
595        if (hashCode < 0) {
596            hashCode = -hashCode;
597        }
598       
599        component.params.add(new String[] { "hash", Integer.toString(hashCode, 16) });
600
601        return component;
602    }   
603
604    /**
605     * <p>
606     * used to dump a list of parameters to the provided print stream
607     * </p>
608     */
609    private void dumpParams(PrintStream out, List<String[]> params, String indent) {
610        for (String[] param : params) {
611            out.print(indent);
612            out.print("<param name=\"");
613            out.print(param[0]);
614            out.print("\" value=\"");
615            out.print(param[1]);
616            out.println("\" />");
617        }
618       
619    }
620   
621    /**
622     * <p>
623     * used to carry all events of a session and to dump it to the output file
624     * </p>
625     */
626    private class Session {
627        private String type;
628        private List<Event> events = new ArrayList<Event>();
629       
630        public void dump(PrintStream out) {
631            out.print("<");
632            out.print(type);
633            out.println(">");
634
635            for (Event event : events) {
636                event.dump(out);
637            }
638           
639            out.print("</");
640            out.print(type);
641            out.println(">");
642        }
643    }
644
645    /**
646     * <p>
647     * used to carry all information about an event and to dump it to the output file
648     * </p>
649     */
650    private class Event {
651        private String id;
652        private List<String[]> params = new ArrayList<String[]>();
653        private Source source;
654       
655        private void dump(PrintStream out) {
656            out.print("<event id=\"");
657            out.print(id);
658            out.println("\">");
659           
660            dumpParams(out, params, " ");
661            source.dump(out);
662           
663            out.println("</event>");
664        }
665    }
666
667    /**
668     * <p>
669     * used to carry all information about a source of an event and to dump it to the output file
670     * </p>
671     */
672    private class Source {
673        private List<String[]> params = new ArrayList<String[]>();
674        private List<Component> components = new ArrayList<Component>();
675
676        private void dump(PrintStream out) {
677            out.println(" <source>");
678           
679            dumpParams(out, params, "  ");
680           
681            for (Component component : components) {
682                component.dump(out);
683            }
684           
685            out.println(" </source>");
686        }
687    }
688   
689    /**
690     * <p>
691     * used to carry all information about a component of a source and to dump it to the output file
692     * </p>
693     */
694    private class Component {
695        protected List<String[]> params = new ArrayList<String[]>();
696       
697        protected void dump(PrintStream out) {
698            out.println("  <component>");
699            dumpParams(out, params, "   ");
700            out.println("  </component>");
701        }
702
703        public boolean equals(Object other) {
704            if (this == other) {
705                return true;
706            }
707           
708            if (!(other instanceof Component)) {
709                return false;
710            }
711           
712            Component otherComp = (Component) other;
713           
714            boolean allParamsEqual = (params.size() == otherComp.params.size());
715           
716            if (allParamsEqual) {
717                for (int i = 0; i < params.size(); i++) {
718                    if (!params.get(i)[0].equals(otherComp.params.get(i)[0]) &&
719                        !params.get(i)[1].equals(otherComp.params.get(i)[1]))
720                    {
721                        allParamsEqual = false;
722                        break;
723                    }
724                }
725            }
726           
727            return allParamsEqual;
728        }
729    }
730
731    /**
732     * <p>
733     * represents a specific component, which was read from the toString parameter of a source
734     * </p>
735     */
736    private class ComponentFromToString extends Component {
737        private int x;
738        private int y;
739        private int width;
740        private int height;
741       
742        @Override
743        protected void dump(PrintStream out) {
744            out.println("  <component>");
745           
746            out.print("   ");
747            out.print("<param name=\"x\" value=\"");
748            out.print(x);
749            out.println("\" />");
750
751            out.print("   ");
752            out.print("<param name=\"y\" value=\"");
753            out.print(y);
754            out.println("\" />");
755
756            out.print("   ");
757            out.print("<param name=\"width\" value=\"");
758            out.print(width);
759            out.println("\" />");
760
761            out.print("   ");
762            out.print("<param name=\"height\" value=\"");
763            out.print(height);
764            out.println("\" />");
765
766            dumpParams(out, params, "   ");
767            out.println("  </component>");
768        }
769       
770        /*public boolean equals(Object other) {
771            if (this == other) {
772                return true;
773            }
774           
775            if (!(other instanceof ComponentFromToString)) {
776                return false;
777            }
778           
779            ComponentFromToString otherComp = (ComponentFromToString) other;
780           
781            // ignore the coordinates and the width as it may change over time
782            boolean allParamsEqual =
783                (height == otherComp.height) && (params.size() == otherComp.params.size());
784           
785            if (allParamsEqual) {
786                for (int i = 0; i < params.size(); i++) {
787                    if (!"x".equals(params.get(i)[0]) && !"y".equals(params.get(i)[0]) &&
788                        !"width".equals(params.get(i)[0]) && !"height".equals(params.get(i)[0]) &&
789                        !"index".equals(params.get(i)[0]) && !"hash".equals(params.get(i)[0]) &&
790                        !params.get(i)[0].equals(otherComp.params.get(i)[0]) &&
791                        !params.get(i)[1].equals(otherComp.params.get(i)[1]))
792                    {
793                        allParamsEqual = false;
794                        break;
795                    }
796                }
797            }
798           
799            return allParamsEqual;
800        }*/
801    }
802
803    /**
804     *
805     */
806    /*private class SourceGroup {
807        protected List<Component> parentComponents = new ArrayList<Component>();
808        protected Map<Integer, List<ComponentFromToString>> leafComponents =
809            new HashMap<Integer, List<ComponentFromToString>>();
810
811        private void dump(PrintStream out) {
812            out.println("source group:");
813           
814            for (Map.Entry<Integer, List<ComponentFromToString>> entry : leafComponents.entrySet())
815            {
816                out.print("    x-coordinate = ");
817                out.println(entry.getKey());
818                for (ComponentFromToString leafComponent : entry.getValue()) {
819                    leafComponent.dump(out);
820                }
821               
822                out.println();
823            }
824        }
825    }*/
826
827}
Note: See TracBrowser for help on using the repository browser.