diff options
Diffstat (limited to 'src/jdiff/APIHandler.java')
-rwxr-xr-x | src/jdiff/APIHandler.java | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/src/jdiff/APIHandler.java b/src/jdiff/APIHandler.java new file mode 100755 index 0000000..be1a6fc --- /dev/null +++ b/src/jdiff/APIHandler.java @@ -0,0 +1,362 @@ +package jdiff; + +import java.io.*; +import java.util.*; + +/* For SAX parsing in APIHandler */ +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 an API object. + * + * See the file LICENSE.txt for copyright details. + * @author Matthew Doar, mdoar@pobox.com + */ +class APIHandler extends DefaultHandler { + + /** The API object which is populated from the XML file. */ + public API api_; + + /** Default constructor. */ + public APIHandler(API api, boolean createGlobalComments) { + api_ = api; + createGlobalComments_ = createGlobalComments; + tagStack = new LinkedList(); + } + + /** If set, then check that each comment is a sentence. */ + public static boolean checkIsSentence = false; + + /** + * Contains the name of the current package element type + * where documentation is being added. Also used as the level + * at which to add documentation into an element, i.e. class-level + * or package-level. + */ + private String currentElement = null; + + /** If set, then create the global list of comments. */ + private boolean createGlobalComments_ = false; + + /** Set if inside a doc element. */ + private boolean inDoc = false; + + /** The current comment text being assembled. */ + private String currentText = null; + + /** The current text from deprecation, null if empty. */ + private String currentDepText = null; + + /** + * The stack of SingleComment objects awaiting the comment text + * currently being assembled. + */ + private LinkedList tagStack = null; + + /** Called at the start of the document. */ + public void startDocument() { + } + + /** Called when the end of the document is reached. */ + public void endDocument() { + if (trace) + api_.dump(); + System.out.println(" finished"); + } + + /** Called when a new element is started. */ + 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("api") == 0) { + String apiName = attributes.getValue("name"); + String version = attributes.getValue("jdversion"); // Not used yet + XMLToAPI.nameAPI(apiName); + } else if (localName.compareTo("package") == 0) { + currentElement = localName; + String pkgName = attributes.getValue("name"); + XMLToAPI.addPackage(pkgName); + } else if (localName.compareTo("class") == 0) { + currentElement = localName; + String className = attributes.getValue("name"); + String parentName = attributes.getValue("extends"); + boolean isAbstract = false; + if (attributes.getValue("abstract").compareTo("true") == 0) + isAbstract = true; + XMLToAPI.addClass(className, parentName, isAbstract, getModifiers(attributes)); + } else if (localName.compareTo("interface") == 0) { + currentElement = localName; + String className = attributes.getValue("name"); + String parentName = attributes.getValue("extends"); + boolean isAbstract = false; + if (attributes.getValue("abstract").compareTo("true") == 0) + isAbstract = true; + XMLToAPI.addInterface(className, parentName, isAbstract, getModifiers(attributes)); + } else if (localName.compareTo("implements") == 0) { + String interfaceName = attributes.getValue("name"); + XMLToAPI.addImplements(interfaceName); + } else if (localName.compareTo("constructor") == 0) { + currentElement = localName; + String ctorType = attributes.getValue("type"); + XMLToAPI.addCtor(ctorType, getModifiers(attributes)); + } else if (localName.compareTo("method") == 0) { + currentElement = localName; + String methodName = attributes.getValue("name"); + String returnType = attributes.getValue("return"); + boolean isAbstract = false; + if (attributes.getValue("abstract").compareTo("true") == 0) + isAbstract = true; + boolean isNative = false; + if (attributes.getValue("native").compareTo("true") == 0) + isNative = true; + boolean isSynchronized = false; + if (attributes.getValue("synchronized").compareTo("true") == 0) + isSynchronized = true; + XMLToAPI.addMethod(methodName, returnType, isAbstract, isNative, + isSynchronized, getModifiers(attributes)); + } else if (localName.compareTo("field") == 0) { + currentElement = localName; + String fieldName = attributes.getValue("name"); + String fieldType = attributes.getValue("type"); + boolean isTransient = false; + if (attributes.getValue("transient").compareTo("true") == 0) + isTransient = true; + boolean isVolatile = false; + if (attributes.getValue("volatile").compareTo("true") == 0) + isVolatile = true; + String value = attributes.getValue("value"); + XMLToAPI.addField(fieldName, fieldType, isTransient, isVolatile, + value, getModifiers(attributes)); + } else if (localName.compareTo("param") == 0) { + String paramName = attributes.getValue("name"); + String paramType = attributes.getValue("type"); + XMLToAPI.addParam(paramName, paramType); + } else if (localName.compareTo("exception") == 0) { + String paramName = attributes.getValue("name"); + String paramType = attributes.getValue("type"); + XMLToAPI.addException(paramName, paramType, currentElement); + } else if (localName.compareTo("doc") == 0) { + inDoc = true; + currentText = null; + } else { + if (inDoc) { + // Start of an element, probably an HTML element + addStartTagToText(localName, attributes); + } else { + System.out.println("Error: unknown element type: " + localName); + System.exit(-1); + } + } + } + + /** Called when the end of an element is reached. */ + public void endElement(java.lang.String uri, java.lang.String localName, + java.lang.String qName) { + if (localName.equals("")) + localName = qName; + // Deal with the end of doc blocks + if (localName.compareTo("doc") == 0) { + inDoc = false; + // Add the assembled comment text to the appropriate current + // program element, as determined by currentElement. + addTextToComments(); + } else if (inDoc) { + // An element was found inside the HTML text + addEndTagToText(localName); + } else if (currentElement.compareTo("constructor") == 0 && + localName.compareTo("constructor") == 0) { + currentElement = "class"; + } else if (currentElement.compareTo("method") == 0 && + localName.compareTo("method") == 0) { + currentElement = "class"; + } else if (currentElement.compareTo("field") == 0 && + localName.compareTo("field") == 0) { + currentElement = "class"; + } else if (currentElement.compareTo("class") == 0 || + currentElement.compareTo("interface") == 0) { + // Feature request 510307 and bug 517383: duplicate comment ids. + // The end of a member element leaves the currentElement at the + // "class" level, but the next class may in fact be an interface + // and so the currentElement here will be "interface". + if (localName.compareTo("class") == 0 || + localName.compareTo("interface") == 0) { + currentElement = "package"; + } + } + } + + /** Called to process text. */ + public void characters(char[] ch, int start, int length) { + if (inDoc) { + 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 the + * current program element. + */ + public void addTextToComments() { + // Eliminate any whitespace at each end of the text. + currentText = currentText.trim(); + // Convert any @link tags to HTML links. + if (convertAtLinks) { + currentText = Comments.convertAtLinks(currentText, currentElement, + api_.currPkg_, api_.currClass_); + } + // Check that it is a sentence + if (checkIsSentence && !currentText.endsWith(".") && + currentText.compareTo(Comments.placeHolderText) != 0) { + System.out.println("Warning: text of comment does not end in a period: " + currentText); + } + // The construction of the commentID assumes that the + // documentation is the final element to be parsed. The format matches + // the format used in the report generator to look up comments in the + // the existingComments object. + String commentID = null; + // Add this comment to the current API element. + if (currentElement.compareTo("package") == 0) { + api_.currPkg_.doc_ = currentText; + commentID = api_.currPkg_.name_; + } else if (currentElement.compareTo("class") == 0 || + currentElement.compareTo("interface") == 0) { + api_.currClass_.doc_ = currentText; + commentID = api_.currPkg_.name_ + "." + api_.currClass_.name_; + } else if (currentElement.compareTo("constructor") == 0) { + api_.currCtor_.doc_ = currentText; + commentID = api_.currPkg_.name_ + "." + api_.currClass_.name_ + + ".ctor_changed("; + if (api_.currCtor_.type_.compareTo("void") == 0) + commentID = commentID + ")"; + else + commentID = commentID + api_.currCtor_.type_ + ")"; + } else if (currentElement.compareTo("method") == 0) { + api_.currMethod_.doc_ = currentText; + commentID = api_.currPkg_.name_ + "." + api_.currClass_.name_ + + "." + api_.currMethod_.name_ + "_changed(" + + api_.currMethod_.getSignature() + ")"; + } else if (currentElement.compareTo("field") == 0) { + api_.currField_.doc_ = currentText; + commentID = api_.currPkg_.name_ + "." + api_.currClass_.name_ + + "." + api_.currField_.name_; + } + // Add to the list of possible comments for use when an + // element has changed (not removed or added). + if (createGlobalComments_ && commentID != null) { + String ct = currentText; + // Use any deprecation text as the possible comment, ignoring + // any other comment text. + if (currentDepText != null) { + ct = currentDepText; + currentDepText = null; // Never reuse it. Bug 469794 + } + String ctOld = (String)(Comments.allPossibleComments.put(commentID, ct)); + if (ctOld != null) { + System.out.println("Error: duplicate comment id: " + commentID); + System.exit(5); + } + } + } + + /** + * 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 + ">"; + } + + /** Extra modifiers which are common to all program elements. */ + public Modifiers getModifiers(Attributes attributes) { + Modifiers modifiers = new Modifiers(); + modifiers.isStatic = false; + if (attributes.getValue("static").compareTo("true") == 0) + modifiers.isStatic = true; + modifiers.isFinal = false; + if (attributes.getValue("final").compareTo("true") == 0) + modifiers.isFinal = true; + modifiers.isDeprecated = false; + String cdt = attributes.getValue("deprecated"); + if (cdt.compareTo("not deprecated") == 0) { + modifiers.isDeprecated = false; + currentDepText = null; + } else if (cdt.compareTo("deprecated, no comment") == 0) { + modifiers.isDeprecated = true; + currentDepText = null; + } else { + modifiers.isDeprecated = true; + currentDepText = API.showHTMLTags(cdt); + } + modifiers.visibility = attributes.getValue("visibility"); + return modifiers; + } + + public void warning(SAXParseException e) { + System.out.println("Warning (" + e.getLineNumber() + "): parsing XML API file:" + e); + e.printStackTrace(); + } + + public void error(SAXParseException e) { + System.out.println("Error (" + e.getLineNumber() + "): parsing XML API file:" + e); + e.printStackTrace(); + System.exit(1); + } + + public void fatalError(SAXParseException e) { + System.out.println("Fatal Error (" + e.getLineNumber() + "): parsing XML API file:" + e); + e.printStackTrace(); + System.exit(1); + } + + /** + * If set, then attempt to convert @link tags to HTML links. + * A few of the HTML links may be broken links. + */ + private static boolean convertAtLinks = true; + + /** Set to enable increased logging verbosity for debugging. */ + private static boolean trace = false; + +} |