aboutsummaryrefslogtreecommitdiff
path: root/src/jdiff/CommentsHandler.java
blob: 8061fbe63ee251d1260a91acf8cc0f83cacae44a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
package jdiff;

import java.io.*;
import java.util.*;

/* For SAX XML parsing */
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Handle the parsing of an XML file and the generation of a Comments object.
 *
 * All HTML written for the comments sections in the report must
 * use tags such as <p/> rather than just <p>, since the XML
 * parser used requires that or matching end elements.
 *
 * From http://www.w3.org/TR/2000/REC-xhtml1-20000126:
 * "Empty elements must either have an end tag or the start tag must end with /<". 
 *
 * See the file LICENSE.txt for copyright details.
 * @author Matthew Doar, mdoar@pobox.com
 */
class CommentsHandler extends DefaultHandler {

    /** The Comments object which is populated from the XML file. */
    public Comments comments_ = null;

    /** The current SingleComment object being populated. */
    private List currSingleComment_ = null; // SingleComment[]

    /** Set if in text. */
    private boolean inText = false;

    /** The current text which is being assembled from chunks. */
    private String currentText = null;
    
    /** The stack of SingleComments still waiting for comment text. */
    private LinkedList tagStack = null;

    /** Default constructor. */
    public CommentsHandler(Comments comments) {
        comments_ = comments;
        tagStack = new LinkedList();
    }   

    public void startDocument() {
    }
    
    public void endDocument() {
        if (trace)
            comments_.dump();
    }

    public void startElement(java.lang.String uri, java.lang.String localName,
                             java.lang.String qName, Attributes attributes) {
	// The change to JAXP compliance produced this change.
	if (localName.equals(""))
	    localName = qName;
        if (localName.compareTo("comments") == 0) {
            String commentsName = attributes.getValue("name");
            String version = attributes.getValue("jdversion"); // Not used yet
            if (commentsName == null) {
                System.out.println("Error: no identifier found in the comments XML file.");
                System.exit(3);
            }
            // Check the given names against the names of the APIs
            int idx1 = JDiff.oldFileName.lastIndexOf('.');
            int idx2 = JDiff.newFileName.lastIndexOf('.');
            String filename2 = JDiff.oldFileName.substring(0, idx1) + 
                "_to_" + JDiff.newFileName.substring(0, idx2);
            if (filename2.compareTo(commentsName) != 0) {
                System.out.println("Warning: API identifier in the comments XML file (" + filename2 + ") differs from the name of the file.");
            }
        } else if (localName.compareTo("comment") == 0) {
            currSingleComment_ = new ArrayList(); // SingleComment[];
        } else if (localName.compareTo("identifier") == 0) {
            // May have multiple identifiers for one comment's text
            String id = attributes.getValue("id");
            SingleComment newComment = new SingleComment(id, null);
            // Store it here until we can add text to it
            currSingleComment_.add(newComment);
        } else if (localName.compareTo("text") == 0) {
            inText = true;
            currentText = null;
        } else {
            if (inText) {
                // Start of an element, probably an HTML element
                addStartTagToText(localName, attributes);
            } else {
                System.out.println("Error: unknown element type: " + localName);
                System.exit(-1);
            }
        }
    }
    
    public void endElement(java.lang.String uri, java.lang.String localName, 
                           java.lang.String qName) {
	if (localName.equals(""))
	    localName = qName;
        if (localName.compareTo("text") == 0) {
            inText = false;
            addTextToComments();
        } else if (inText) {
            addEndTagToText(localName);
        }

    }
    
    /** Deal with a chunk of text. The text may come in multiple chunks. */
    public void characters(char[] ch, int start, int length) {
        if (inText) {
            String chunk = new String(ch, start, length);
            if (currentText == null)
                currentText = chunk;
            else
                currentText += chunk;
        }
    }

    /** 
     * Trim the current text, check it is a sentence and add it to all 
     * the comments which are waiting for it. 
     */
    public void addTextToComments() {
        // Eliminate any whitespace at each end of the text.
        currentText = currentText.trim();
        // Check that it is a sentence
        if (!currentText.endsWith(".") &&
            !currentText.endsWith("?") &&
            !currentText.endsWith("!") && 
            currentText.compareTo(Comments.placeHolderText) != 0) {
            System.out.println("Warning: text of comment does not end in a period: " + currentText);
        }
        // Add this comment to all the SingleComments waiting for it
        Iterator iter = currSingleComment_.iterator();
        while (iter.hasNext()) {
            SingleComment currComment = (SingleComment)(iter.next());
            if (currComment.text_ == null)
                currComment.text_ = currentText;
            else
                currComment.text_ += currentText;
            comments_.addComment(currComment);
        }
    }

    /** 
     * Add the start tag to the current comment text. 
     */
    public void addStartTagToText(String localName, Attributes attributes) {
        // Need to insert the HTML tag into the current text
        String currentHTMLTag = localName;
        // Save the tag in a stack
        tagStack.add(currentHTMLTag);
        String tag = "<" + currentHTMLTag;
        // Now add all the attributes into the current text
        int len = attributes.getLength();
        for (int i = 0; i < len; i++) {
            String name = attributes.getLocalName(i);
            String value = attributes.getValue(i);
            tag += " " + name + "=\"" + value+ "\"";
        }

        // End the tag
        if (Comments.isMinimizedTag(currentHTMLTag)) {
            tag += "/>";
        } else {
            tag += ">";
        }
        // Now insert the HTML tag into the current text
        if (currentText == null)
            currentText = tag;
        else
            currentText += tag;
    }

    /** 
     * Add the end tag to the current comment text. 
     */
    public void addEndTagToText(String localName) {
        // Close the current HTML tag
        String currentHTMLTag = (String)(tagStack.removeLast());
        if (!Comments.isMinimizedTag(currentHTMLTag))
            currentText += "</" + currentHTMLTag + ">";
    }

    public void warning(SAXParseException e) {
        System.out.println("Warning (" + e.getLineNumber() + "): parsing XML comments file:" + e);
        e.printStackTrace();
    }

    public void error(SAXParseException e) {
        System.out.println("Error (" + e.getLineNumber() + "): parsing XML comments file:" + e);
        e.printStackTrace();
        System.exit(1);
    }
    
    public void fatalError(SAXParseException e) {
        System.out.println("Fatal Error (" + e.getLineNumber() + "): parsing XML comments file:" + e);
        e.printStackTrace();
        System.exit(1);
    }    

    /** Set to enable increased logging verbosity for debugging. */
    private static final boolean trace = false;

}