| 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 https://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; | |
| } | |