1 package jdiff; 2 3 import java.io.*; 4 import java.util.*; 5 6 /* For SAX XML parsing */ 7 import org.xml.sax.Attributes; 8 import org.xml.sax.SAXException; 9 import org.xml.sax.SAXParseException; 10 import org.xml.sax.XMLReader; 11 import org.xml.sax.helpers.DefaultHandler; 12 13 /** 14 * Handle the parsing of an XML file and the generation of a Comments object. 15 * 16 * All HTML written for the comments sections in the report must 17 * use tags such as <p/> rather than just <p>, since the XML 18 * parser used requires that or matching end elements. 19 * 20 * From http://www.w3.org/TR/2000/REC-xhtml1-20000126: 21 * "Empty elements must either have an end tag or the start tag must end with /<". 22 * 23 * See the file LICENSE.txt for copyright details. 24 * @author Matthew Doar, mdoar (at) pobox.com 25 */ 26 class CommentsHandler extends DefaultHandler { 27 28 /** The Comments object which is populated from the XML file. */ 29 public Comments comments_ = null; 30 31 /** The current SingleComment object being populated. */ 32 private List currSingleComment_ = null; // SingleComment[] 33 34 /** Set if in text. */ 35 private boolean inText = false; 36 37 /** The current text which is being assembled from chunks. */ 38 private String currentText = null; 39 40 /** The stack of SingleComments still waiting for comment text. */ 41 private LinkedList tagStack = null; 42 43 /** Default constructor. */ 44 public CommentsHandler(Comments comments) { 45 comments_ = comments; 46 tagStack = new LinkedList(); 47 } 48 49 public void startDocument() { 50 } 51 52 public void endDocument() { 53 if (trace) 54 comments_.dump(); 55 } 56 57 public void startElement(java.lang.String uri, java.lang.String localName, 58 java.lang.String qName, Attributes attributes) { 59 // The change to JAXP compliance produced this change. 60 if (localName.equals("")) 61 localName = qName; 62 if (localName.compareTo("comments") == 0) { 63 String commentsName = attributes.getValue("name"); 64 String version = attributes.getValue("jdversion"); // Not used yet 65 if (commentsName == null) { 66 System.out.println("Error: no identifier found in the comments XML file."); 67 System.exit(3); 68 } 69 // Check the given names against the names of the APIs 70 int idx1 = JDiff.oldFileName.lastIndexOf('.'); 71 int idx2 = JDiff.newFileName.lastIndexOf('.'); 72 String filename2 = JDiff.oldFileName.substring(0, idx1) + 73 "_to_" + JDiff.newFileName.substring(0, idx2); 74 if (filename2.compareTo(commentsName) != 0) { 75 System.out.println("Warning: API identifier in the comments XML file (" + filename2 + ") differs from the name of the file."); 76 } 77 } else if (localName.compareTo("comment") == 0) { 78 currSingleComment_ = new ArrayList(); // SingleComment[]; 79 } else if (localName.compareTo("identifier") == 0) { 80 // May have multiple identifiers for one comment's text 81 String id = attributes.getValue("id"); 82 SingleComment newComment = new SingleComment(id, null); 83 // Store it here until we can add text to it 84 currSingleComment_.add(newComment); 85 } else if (localName.compareTo("text") == 0) { 86 inText = true; 87 currentText = null; 88 } else { 89 if (inText) { 90 // Start of an element, probably an HTML element 91 addStartTagToText(localName, attributes); 92 } else { 93 System.out.println("Error: unknown element type: " + localName); 94 System.exit(-1); 95 } 96 } 97 } 98 99 public void endElement(java.lang.String uri, java.lang.String localName, 100 java.lang.String qName) { 101 if (localName.equals("")) 102 localName = qName; 103 if (localName.compareTo("text") == 0) { 104 inText = false; 105 addTextToComments(); 106 } else if (inText) { 107 addEndTagToText(localName); 108 } 109 110 } 111 112 /** Deal with a chunk of text. The text may come in multiple chunks. */ 113 public void characters(char[] ch, int start, int length) { 114 if (inText) { 115 String chunk = new String(ch, start, length); 116 if (currentText == null) 117 currentText = chunk; 118 else 119 currentText += chunk; 120 } 121 } 122 123 /** 124 * Trim the current text, check it is a sentence and add it to all 125 * the comments which are waiting for it. 126 */ 127 public void addTextToComments() { 128 // Eliminate any whitespace at each end of the text. 129 currentText = currentText.trim(); 130 // Check that it is a sentence 131 if (!currentText.endsWith(".") && 132 !currentText.endsWith("?") && 133 !currentText.endsWith("!") && 134 currentText.compareTo(Comments.placeHolderText) != 0) { 135 System.out.println("Warning: text of comment does not end in a period: " + currentText); 136 } 137 // Add this comment to all the SingleComments waiting for it 138 Iterator iter = currSingleComment_.iterator(); 139 while (iter.hasNext()) { 140 SingleComment currComment = (SingleComment)(iter.next()); 141 if (currComment.text_ == null) 142 currComment.text_ = currentText; 143 else 144 currComment.text_ += currentText; 145 comments_.addComment(currComment); 146 } 147 } 148 149 /** 150 * Add the start tag to the current comment text. 151 */ 152 public void addStartTagToText(String localName, Attributes attributes) { 153 // Need to insert the HTML tag into the current text 154 String currentHTMLTag = localName; 155 // Save the tag in a stack 156 tagStack.add(currentHTMLTag); 157 String tag = "<" + currentHTMLTag; 158 // Now add all the attributes into the current text 159 int len = attributes.getLength(); 160 for (int i = 0; i < len; i++) { 161 String name = attributes.getLocalName(i); 162 String value = attributes.getValue(i); 163 tag += " " + name + "=\"" + value+ "\""; 164 } 165 166 // End the tag 167 if (Comments.isMinimizedTag(currentHTMLTag)) { 168 tag += "/>"; 169 } else { 170 tag += ">"; 171 } 172 // Now insert the HTML tag into the current text 173 if (currentText == null) 174 currentText = tag; 175 else 176 currentText += tag; 177 } 178 179 /** 180 * Add the end tag to the current comment text. 181 */ 182 public void addEndTagToText(String localName) { 183 // Close the current HTML tag 184 String currentHTMLTag = (String)(tagStack.removeLast()); 185 if (!Comments.isMinimizedTag(currentHTMLTag)) 186 currentText += "</" + currentHTMLTag + ">"; 187 } 188 189 public void warning(SAXParseException e) { 190 System.out.println("Warning (" + e.getLineNumber() + "): parsing XML comments file:" + e); 191 e.printStackTrace(); 192 } 193 194 public void error(SAXParseException e) { 195 System.out.println("Error (" + e.getLineNumber() + "): parsing XML comments file:" + e); 196 e.printStackTrace(); 197 System.exit(1); 198 } 199 200 public void fatalError(SAXParseException e) { 201 System.out.println("Fatal Error (" + e.getLineNumber() + "): parsing XML comments file:" + e); 202 e.printStackTrace(); 203 System.exit(1); 204 } 205 206 /** Set to enable increased logging verbosity for debugging. */ 207 private static final boolean trace = false; 208 209 } 210 211