• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package jdiff;
2 
3 import java.io.*;
4 import java.util.*;
5 
6 /* For SAX parsing in APIHandler */
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 an API object.
15  *
16  * See the file LICENSE.txt for copyright details.
17  * @author Matthew Doar, mdoar@pobox.com
18  */
19 class APIHandler extends DefaultHandler {
20 
21     /** The API object which is populated from the XML file. */
22     public API api_;
23 
24     /** Default constructor. */
APIHandler(API api, boolean createGlobalComments)25     public APIHandler(API api, boolean createGlobalComments) {
26         api_ = api;
27         createGlobalComments_ = createGlobalComments;
28         tagStack = new LinkedList();
29     }
30 
31     /** If set, then check that each comment is a sentence. */
32     public static boolean checkIsSentence = false;
33 
34     /**
35      * Contains the name of the current package element type
36      * where documentation is being added. Also used as the level
37      * at which to add documentation into an element, i.e. class-level
38      * or package-level.
39      */
40     private String currentElement = null;
41 
42     /** If set, then create the global list of comments. */
43     private boolean createGlobalComments_ = false;
44 
45     /** Set if inside a doc element. */
46     private boolean inDoc = false;
47 
48     /** The current comment text being assembled. */
49     private String currentText = null;
50 
51     /** The current text from deprecation, null if empty. */
52     private String currentDepText = null;
53 
54     /**
55      * The stack of SingleComment objects awaiting the comment text
56      * currently being assembled.
57      */
58     private LinkedList tagStack = null;
59 
60     /** Called at the start of the document. */
startDocument()61     public void startDocument() {
62     }
63 
64     /** Called when the end of the document is reached. */
endDocument()65     public void endDocument() {
66         if (trace)
67             api_.dump();
68         System.out.println(" finished");
69     }
70 
71     /** Called when a new element is started. */
startElement(java.lang.String uri, java.lang.String localName, java.lang.String qName, Attributes attributes)72     public void startElement(java.lang.String uri, java.lang.String localName,
73                              java.lang.String qName, Attributes attributes) {
74 	 // The change to JAXP compliance produced this change.
75 	if (localName.equals(""))
76 	    localName = qName;
77         if (localName.compareTo("api") == 0) {
78             String apiName = attributes.getValue("name");
79             String version = attributes.getValue("jdversion"); // Not used yet
80             XMLToAPI.nameAPI(apiName);
81         } else if (localName.compareTo("package") == 0) {
82             currentElement = localName;
83             String pkgName = attributes.getValue("name");
84             XMLToAPI.addPackage(pkgName);
85         } else if (localName.compareTo("class") == 0) {
86             currentElement = localName;
87             String className = attributes.getValue("name");
88             String parentName = attributes.getValue("extends");
89             boolean isAbstract = false;
90             if (attributes.getValue("abstract").compareTo("true") == 0)
91                 isAbstract = true;
92             XMLToAPI.addClass(className, parentName, isAbstract, getModifiers(attributes));
93         } else if (localName.compareTo("interface") == 0) {
94             currentElement = localName;
95             String className = attributes.getValue("name");
96             String parentName = attributes.getValue("extends");
97             boolean isAbstract = false;
98             if (attributes.getValue("abstract").compareTo("true") == 0)
99                 isAbstract = true;
100             XMLToAPI.addInterface(className, parentName, isAbstract, getModifiers(attributes));
101         } else if (localName.compareTo("implements") == 0) {
102             String interfaceName = attributes.getValue("name");
103             XMLToAPI.addImplements(interfaceName);
104         } else if (localName.compareTo("constructor") == 0) {
105             currentElement = localName;
106             String ctorName = attributes.getValue("name");
107             String ctorType = attributes.getValue("type");
108             XMLToAPI.addCtor(ctorName, ctorType, getModifiers(attributes));
109         } else if (localName.compareTo("method") == 0) {
110             currentElement = localName;
111             String methodName = attributes.getValue("name");
112             String returnType = attributes.getValue("return");
113             boolean isAbstract = false;
114             if (attributes.getValue("abstract").compareTo("true") == 0)
115                 isAbstract = true;
116             boolean isNative = false;
117             if (attributes.getValue("native").compareTo("true") == 0)
118                 isNative = true;
119             boolean isSynchronized = false;
120             if (attributes.getValue("synchronized").compareTo("true") == 0)
121                 isSynchronized = true;
122             XMLToAPI.addMethod(methodName, returnType, isAbstract, isNative,
123                                isSynchronized, getModifiers(attributes));
124         } else if (localName.compareTo("field") == 0) {
125             currentElement = localName;
126             String fieldName = attributes.getValue("name");
127             String fieldType = attributes.getValue("type");
128             boolean isTransient = false;
129             if (attributes.getValue("transient").compareTo("true") == 0)
130                 isTransient = true;
131             boolean isVolatile = false;
132             if (attributes.getValue("volatile").compareTo("true") == 0)
133                 isVolatile = true;
134             String value = attributes.getValue("value");
135             XMLToAPI.addField(fieldName, fieldType, isTransient, isVolatile,
136                               value, getModifiers(attributes));
137         } else if (localName.compareTo("param") == 0 || localName.compareTo("parameter") == 0) {
138             String paramName = attributes.getValue("name");
139             String paramType = attributes.getValue("type");
140             XMLToAPI.addParam(paramName, paramType, currentElement.compareTo("constructor") == 0);
141         } else if (localName.compareTo("exception") == 0) {
142             String paramName = attributes.getValue("name");
143             String paramType = attributes.getValue("type");
144             XMLToAPI.addException(paramName, paramType, currentElement);
145         } else if (localName.compareTo("doc") == 0) {
146             inDoc = true;
147             currentText = null;
148         } else {
149             if (inDoc) {
150                 // Start of an element, probably an HTML element
151                 addStartTagToText(localName, attributes);
152             } else {
153                 System.out.println("Error: unknown element type: " + localName);
154                 System.exit(-1);
155             }
156         }
157     }
158 
159     /** Called when the end of an element is reached. */
endElement(java.lang.String uri, java.lang.String localName, java.lang.String qName)160     public void endElement(java.lang.String uri, java.lang.String localName,
161                            java.lang.String qName) {
162         if (localName.equals(""))
163             localName = qName;
164         // Deal with the end of doc blocks
165         if (localName.compareTo("doc") == 0) {
166             inDoc = false;
167             // Add the assembled comment text to the appropriate current
168             // program element, as determined by currentElement.
169             addTextToComments();
170         } else if (inDoc) {
171             // An element was found inside the HTML text
172             addEndTagToText(localName);
173         } else if (currentElement == null) {
174             // No elements were found, nothing to do here
175         } else if (currentElement.compareTo("constructor") == 0 &&
176                    localName.compareTo("constructor") == 0) {
177             currentElement = "class";
178         } else if (currentElement.compareTo("method") == 0 &&
179                    localName.compareTo("method") == 0) {
180             currentElement = "class";
181         } else if (currentElement.compareTo("field") == 0 &&
182                    localName.compareTo("field") == 0) {
183             currentElement = "class";
184         } else if (currentElement.compareTo("class") == 0 ||
185                    currentElement.compareTo("interface") == 0) {
186             // Feature request 510307 and bug 517383: duplicate comment ids.
187             // The end of a member element leaves the currentElement at the
188             // "class" level, but the next class may in fact be an interface
189             // and so the currentElement here will be "interface".
190             if (localName.compareTo("class") == 0 ||
191                 localName.compareTo("interface") == 0) {
192                 currentElement = "package";
193             }
194         }
195     }
196 
197     /** Called to process text. */
characters(char[] ch, int start, int length)198     public void characters(char[] ch, int start, int length) {
199          if (inDoc) {
200             String chunk = new String(ch, start, length);
201             if (currentText == null)
202                 currentText = chunk;
203             else
204                 currentText += chunk;
205          }
206     }
207 
208     /**
209      * Trim the current text, check it is a sentence and add it to the
210      * current program element.
211      */
addTextToComments()212     public void addTextToComments() {
213         // Eliminate any whitespace at each end of the text.
214         currentText = currentText.trim();
215         // Convert any @link tags to HTML links.
216         if (convertAtLinks) {
217             currentText = Comments.convertAtLinks(currentText, currentElement,
218                                                   api_.currPkg_, api_.currClass_);
219         }
220         // Check that it is a sentence
221         if (checkIsSentence && !currentText.endsWith(".") &&
222             currentText.compareTo(Comments.placeHolderText) != 0) {
223             System.out.println("Warning: text of comment does not end in a period: " + currentText);
224         }
225         // The construction of the commentID assumes that the
226         // documentation is the final element to be parsed. The format matches
227         // the format used in the report generator to look up comments in the
228         // the existingComments object.
229         String commentID = null;
230         // Add this comment to the current API element.
231         if (currentElement.compareTo("package") == 0) {
232             api_.currPkg_.doc_ = currentText;
233             commentID = api_.currPkg_.name_;
234         } else if (currentElement.compareTo("class") == 0 ||
235                    currentElement.compareTo("interface") == 0) {
236             api_.currClass_.doc_ = currentText;
237             commentID = api_.currPkg_.name_ + "." + api_.currClass_.name_;
238         } else if (currentElement.compareTo("constructor") == 0) {
239             api_.currCtor_.doc_ = currentText;
240             commentID = api_.currPkg_.name_ + "." + api_.currClass_.name_ +
241                 ".ctor_changed(";
242             if (api_.currCtor_.getSignature().compareTo("void") == 0)
243                 commentID = commentID + ")";
244             else
245                 commentID = commentID + api_.currCtor_.getSignature() + ")";
246         } else if (currentElement.compareTo("method") == 0) {
247             api_.currMethod_.doc_ = currentText;
248             commentID = api_.currPkg_.name_ + "." + api_.currClass_.name_ +
249                 "." + api_.currMethod_.name_ + "_changed(" +
250                 api_.currMethod_.getSignature() + ")";
251         } else if (currentElement.compareTo("field") == 0) {
252             api_.currField_.doc_ = currentText;
253             commentID = api_.currPkg_.name_ + "." + api_.currClass_.name_ +
254                 "." + api_.currField_.name_;
255         }
256         // Add to the list of possible comments for use when an
257         // element has changed (not removed or added).
258         if (createGlobalComments_ && commentID != null) {
259             String ct = currentText;
260             // Use any deprecation text as the possible comment, ignoring
261             // any other comment text.
262             if (currentDepText != null) {
263                 ct = currentDepText;
264                 currentDepText = null; // Never reuse it. Bug 469794
265             }
266             String ctOld = (String)(Comments.allPossibleComments.put(commentID, ct));
267             if (ctOld != null) {
268                 System.out.println("Error: duplicate comment id: " + commentID);
269                 System.exit(5);
270             }
271         }
272     }
273 
274     /**
275      * Add the start tag to the current comment text.
276      */
addStartTagToText(String localName, Attributes attributes)277     public void addStartTagToText(String localName, Attributes attributes) {
278         // Need to insert the HTML tag into the current text
279         String currentHTMLTag = localName;
280         // Save the tag in a stack
281         tagStack.add(currentHTMLTag);
282         String tag = "<" + currentHTMLTag;
283         // Now add all the attributes into the current text
284         int len = attributes.getLength();
285         for (int i = 0; i < len; i++) {
286             String name = attributes.getLocalName(i);
287             String value = attributes.getValue(i);
288             tag += " " + name + "=\"" + value+ "\"";
289         }
290 
291         // End the tag
292         if (Comments.isMinimizedTag(currentHTMLTag)) {
293             tag += "/>";
294         } else {
295             tag += ">";
296         }
297         // Now insert the HTML tag into the current text
298         if (currentText == null)
299             currentText = tag;
300         else
301             currentText += tag;
302     }
303 
304     /**
305      * Add the end tag to the current comment text.
306      */
addEndTagToText(String localName)307     public void addEndTagToText(String localName) {
308         // Close the current HTML tag
309         String currentHTMLTag = (String)(tagStack.removeLast());
310         if (!Comments.isMinimizedTag(currentHTMLTag))
311             currentText += "</" + currentHTMLTag + ">";
312     }
313 
314     /** Extra modifiers which are common to all program elements. */
getModifiers(Attributes attributes)315     public Modifiers getModifiers(Attributes attributes) {
316         Modifiers modifiers = new Modifiers();
317         modifiers.isStatic = false;
318         if (attributes.getValue("static").compareTo("true") == 0)
319             modifiers.isStatic = true;
320         modifiers.isFinal = false;
321         if (attributes.getValue("final").compareTo("true") == 0)
322             modifiers.isFinal = true;
323         modifiers.isDeprecated = false;
324         String cdt = attributes.getValue("deprecated");
325         if (cdt.compareTo("not deprecated") == 0) {
326             modifiers.isDeprecated = false;
327             currentDepText = null;
328         } else if (cdt.compareTo("deprecated, no comment") == 0) {
329             modifiers.isDeprecated = true;
330             currentDepText = null;
331         } else {
332             modifiers.isDeprecated = true;
333             currentDepText = API.showHTMLTags(cdt);
334         }
335         modifiers.visibility = attributes.getValue("visibility");
336         return modifiers;
337     }
338 
warning(SAXParseException e)339     public void warning(SAXParseException e) {
340         System.out.println("Warning (" + e.getLineNumber() + "): parsing XML API file:" + e);
341         e.printStackTrace();
342     }
343 
error(SAXParseException e)344     public void error(SAXParseException e) {
345         System.out.println("Error (" + e.getLineNumber() + "): parsing XML API file:" + e);
346         e.printStackTrace();
347         System.exit(1);
348     }
349 
fatalError(SAXParseException e)350     public void fatalError(SAXParseException e) {
351         System.out.println("Fatal Error (" + e.getLineNumber() + "): parsing XML API file:" + e);
352         e.printStackTrace();
353         System.exit(1);
354     }
355 
356     /**
357      * If set, then attempt to convert @link tags to HTML links.
358      * A few of the HTML links may be broken links.
359      */
360     private static boolean convertAtLinks = true;
361 
362     /** Set to enable increased logging verbosity for debugging. */
363     private static boolean trace = false;
364 
365 }
366