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

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