FileDocCategorySizeDatePackage
Comments.javaAPI DocAndroid 1.5 API21743Wed May 06 22:41:22 BST 2009jdiff

Comments

public class Comments extends Object
Creates a Comments from an XML file. The Comments object is the internal representation of the comments for the changes. All methods in this class for populating a Comments object are static. See the file LICENSE.txt for copyright details.
author
Matthew Doar, mdoar@pobox.com

Fields Summary
public static Hashtable
allPossibleComments
All the possible comments known about, accessible by the commentID.
private static Comments
oldComments_
The old Comments object which is populated from the file read in.
public List
commentsList_
public static final String
placeHolderText
The text placed into XML comments file where there is no comment yet. It never appears in reports.
private static List
oldAPIList
private static List
newAPIList
private static PrintWriter
outputFile
The file where the XML representing the new Comments object is stored.
Constructors Summary
public Comments()
Default constructor.


       
      
        commentsList_ = new ArrayList(); // SingleComment[]
    
Methods Summary
public voidaddComment(SingleComment comment)
Add the SingleComment object to the list of comments kept by this object.

        commentsList_.add(comment); 
    
public static java.lang.StringconvertAtLinks(java.lang.String text, java.lang.String currentElement, PackageAPI pkg, ClassAPI cls)
Convert @link tags to HTML links.

        if (text == null)
            return null;
	
        StringBuffer result = new StringBuffer();
        
        int state = -1;
        
        final int NORMAL_TEXT = -1;
        final int IN_LINK = 1;
        final int IN_LINK_IDENTIFIER = 2;
        final int IN_LINK_IDENTIFIER_REFERENCE = 3;
        final int IN_LINK_IDENTIFIER_REFERENCE_PARAMS = 6;
        final int IN_LINK_LINKTEXT = 4;
        final int END_OF_LINK = 5;

        StringBuffer identifier = null;
        StringBuffer identifierReference = null;
        StringBuffer linkText = null;
        
        // Figure out relative reference if required.
        String ref = "";
        if (currentElement.compareTo("class") == 0 ||
            currentElement.compareTo("interface") == 0) {
	    ref = pkg.name_ + "." + cls.name_ + ".";
        } else if (currentElement.compareTo("package") == 0) {
	    ref = pkg.name_ + ".";
        }
        ref = ref.replace('.", '/");        
        
        for (int i=0; i < text.length(); i++) {
	    char c = text.charAt( i);
	    char nextChar = i < text.length()-1 ? text.charAt( i+1) : (char)-1;
	    int remainingChars = text.length() - i;
          
	    switch (state) {
	    case NORMAL_TEXT:
		if (c == '{" && remainingChars >= 5) {
		    if ("{@link".equals(text.substring(i, i + 6))) {
			state = IN_LINK;
			identifier = null;
			identifierReference = null;
			linkText = null;
			i += 5;
			continue;
		    }
		}
		result.append( c);
		break;
	    case IN_LINK:
		if (Character.isWhitespace(nextChar)) continue;
		if (nextChar == '}") {
		    // End of the link
		    state = END_OF_LINK;
		} else if (!Character.isWhitespace(nextChar)) {
		    state = IN_LINK_IDENTIFIER;
		}
		break;
            case IN_LINK_IDENTIFIER:
		if (identifier == null) {
		    identifier = new StringBuffer();
		}
            
		if (c == '#") {
		    // We have a reference.
		    state = IN_LINK_IDENTIFIER_REFERENCE;
		    // Don't append #
		    continue;
		} else if (Character.isWhitespace(c)) {
		    // We hit some whitespace: the next character is the beginning
		    // of the link text.
		    state = IN_LINK_LINKTEXT;
		    continue;
		}
		identifier.append(c);              
		// Check for a } that ends the link.
		if (nextChar == '}") {
		    state = END_OF_LINK;
		}
		break;
            case IN_LINK_IDENTIFIER_REFERENCE:
		if (identifierReference == null) {
		    identifierReference = new StringBuffer();
		}
		if (Character.isWhitespace(c)) {
		    state = IN_LINK_LINKTEXT;
		    continue;
		}
		identifierReference.append(c);
              
		if (c == '(") {
		    state = IN_LINK_IDENTIFIER_REFERENCE_PARAMS;
		}
              
		if (nextChar == '}") {
		    state = END_OF_LINK;
		}
		break;
            case IN_LINK_IDENTIFIER_REFERENCE_PARAMS:
		// We're inside the parameters of a reference. Spaces are allowed.
		if (c == ')") {
		    state = IN_LINK_IDENTIFIER_REFERENCE;
		}
		identifierReference.append(c);
		if (nextChar == '}") {
		    state = END_OF_LINK;
		}
		break;
            case IN_LINK_LINKTEXT:
		if (linkText == null) linkText = new StringBuffer();
              
		linkText.append(c);
              
		if (nextChar == '}") {
		    state = END_OF_LINK;
		}
		break;
            case END_OF_LINK:
		if (identifier != null) {
		    result.append("<A HREF=\"");
		    result.append(HTMLReportGenerator.newDocPrefix);
		    result.append(ref);
		    result.append(identifier.toString().replace('.", '/"));
		    result.append(".html");
		    if (identifierReference != null) {
			result.append("#");
			result.append(identifierReference);
		    }
		    result.append("\">");   // target=_top?
                
		    result.append("<TT>");
		    if (linkText != null) {
			result.append(linkText);
		    } else {
			result.append(identifier);
			if (identifierReference != null) {
			    result.append(".");
			    result.append(identifierReference);
			}
		    }
		    result.append("</TT>");
		    result.append("</A>");
		}
		state = NORMAL_TEXT;
		break;
	    }
        }
        return result.toString();
    
public voiddump()
Dump the contents of a Comments object out for inspection.

        Iterator iter = commentsList_.iterator();
        int i = 0;
        while (iter.hasNext()) {
            i++;
            SingleComment currComment = (SingleComment)(iter.next());
            System.out.println("Comment " + i);
            System.out.println("id = " + currComment.id_);
            System.out.println("text = \"" + currComment.text_ + "\"");
            System.out.println("isUsed = " + currComment.isUsed_);
        }        
    
public voidemitComments()
Write the Comments object out in XML.

        Iterator iter = commentsList_.iterator();
        while (iter.hasNext()) {
            SingleComment currComment = (SingleComment)(iter.next());
            if (!currComment.isUsed_)
                outputFile.println("<!-- This comment is no longer used ");
            outputFile.println("<comment>");
            outputFile.println("  <identifier id=\"" + currComment.id_ + "\"/>");
            outputFile.println("  <text>");
            outputFile.println("    " + currComment.text_);
            outputFile.println("  </text>");
            outputFile.println("</comment>");
            if (!currComment.isUsed_)
                outputFile.println("-->");
        }        
    
public voidemitXMLFooter()
Emit the XML footer.

        outputFile.println();
        outputFile.println("</comments>");
    
public voidemitXMLHeader(java.lang.String filename)
Emit the XML header.

        outputFile.println("<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"no\"?>");
        outputFile.println("<comments");
        outputFile.println("  xmlns:xsi='" + RootDocToXML.baseURI + "/2001/XMLSchema-instance'");
        outputFile.println("  xsi:noNamespaceSchemaLocation='comments.xsd'");
        // Extract the identifier from the filename by removing the suffix
        int idx = filename.lastIndexOf('.");
        String apiIdentifier = filename.substring(0, idx);
        // Also remove the output directory and directory separator if present
        if (HTMLReportGenerator.commentsDir != null)
	    apiIdentifier = apiIdentifier.substring(HTMLReportGenerator.commentsDir.length()+1);
        else if (HTMLReportGenerator.outputDir != null)
            apiIdentifier = apiIdentifier.substring(HTMLReportGenerator.outputDir.length()+1);
        // Also remove "user_comments_for_"
        apiIdentifier = apiIdentifier.substring(18);
        outputFile.println("  name=\"" + apiIdentifier + "\"");
        outputFile.println("  jdversion=\"" + JDiff.version + "\">");
        outputFile.println();
        outputFile.println("<!-- Use this file to enter an API change description. For example, when you remove a class, ");
        outputFile.println("     you can enter a comment for that class that points developers to the replacement class. ");
        outputFile.println("     You can also provide a change summary for modified API, to give an overview of the changes ");
        outputFile.println("     why they were made, workarounds, etc.  -->");
        outputFile.println();
        outputFile.println("<!-- When the API diffs report is generated, the comments in this file get added to the tables of ");
        outputFile.println("     removed, added, and modified packages, classes, methods, and fields. This file does not ship ");
        outputFile.println("     with the final report. -->");
        outputFile.println();
        outputFile.println("<!-- The id attribute in an identifier element identifies the change as noted in the report. ");
        outputFile.println("     An id has the form package[.class[.[ctor|method|field].signature]], where [] indicates optional ");
        outputFile.println("     text. A comment element can have multiple identifier elements, which will will cause the same ");
        outputFile.println("     text to appear at each place in the report, but will be converted to separate comments when the ");
        outputFile.println("     comments file is used. -->");
        outputFile.println();
        outputFile.println("<!-- HTML tags in the text field will appear in the report. You also need to close p HTML elements, ");
        outputFile.println("     used for paragraphs - see the top-level documentation. -->");
        outputFile.println();
        outputFile.println("<!-- You can include standard javadoc links in your change descriptions. You can use the @first command  ");
        outputFile.println("     to cause jdiff to include the first line of the API documentation. You also need to close p HTML ");
        outputFile.println("     elements, used for paragraphs - see the top-level documentation. -->");
        outputFile.println();
    
public static java.lang.StringgetComment(jdiff.Comments comments, java.lang.String id)
Return the comment associated with the given id in the Comment object. If there is no such comment, return the placeHolderText.

    
                               
           
        if (comments == null)
            return placeHolderText;
        SingleComment key = new SingleComment(id, null);
        int idx = Collections.binarySearch(comments.commentsList_, key);
        if (idx < 0) {
            return placeHolderText;
        } else {
            int startIdx = comments.commentsList_.indexOf(key);
            int endIdx = comments.commentsList_.indexOf(key);
            int numIdx = endIdx - startIdx + 1;
            if (numIdx != 1) {
                System.out.println("Warning: " + numIdx + " identical ids in the existing comments file. Using the first instance.");
            }
            SingleComment singleComment = (SingleComment)(comments.commentsList_.get(idx));
            // Convert @link tags to links
            return singleComment.text_;
        }
    
public static booleanisMinimizedTag(java.lang.String tag)
Return true if the given HTML tag has no separate end element. If you want to be able to use sloppy HTML in your comments, then you can add the element, e.g. li back into the condition here. However, if you then become more careful and do provide the closing tag, the output is generally just the closing tag, which is incorrect. tag.equalsIgnoreCase("tr") || // Is sometimes minimized tag.equalsIgnoreCase("th") || // Is sometimes minimized tag.equalsIgnoreCase("td") || // Is sometimes minimized tag.equalsIgnoreCase("dt") || // Is sometimes minimized tag.equalsIgnoreCase("dd") || // Is sometimes minimized tag.equalsIgnoreCase("img") || // Is sometimes minimized tag.equalsIgnoreCase("code") || // Is sometimes minimized (error) tag.equalsIgnoreCase("font") || // Is sometimes minimized (error) tag.equalsIgnoreCase("ul") || // Is sometimes minimized tag.equalsIgnoreCase("ol") || // Is sometimes minimized tag.equalsIgnoreCase("li") // Is sometimes minimized


                                                                                                                                               
         
        if (tag.equalsIgnoreCase("p") ||
            tag.equalsIgnoreCase("br") ||
            tag.equalsIgnoreCase("hr")
            ) {
            return true;
	}
        return false;
    
public static voidnoteDifferences(jdiff.Comments oldComments, jdiff.Comments newComments)
Emit messages about which comments are now unused and which are new.

        if (oldComments == null) {
            System.out.println("Note: all the comments have been newly generated");
            return;
        }
        
        // See which comment ids are no longer used and add those entries to 
        // the new comments, marking them as unused.
        Iterator iter = oldComments.commentsList_.iterator();
        while (iter.hasNext()) {
            SingleComment oldComment = (SingleComment)(iter.next());
            int idx = Collections.binarySearch(newComments.commentsList_, oldComment);
            if (idx < 0) {
                System.out.println("Warning: comment \"" + oldComment.id_ + "\" is no longer used.");
                oldComment.isUsed_ = false;
                newComments.commentsList_.add(oldComment);
            }
        }        
        
    
public static jdiff.CommentsreadFile(java.lang.String filename)
Read the file where the XML for comments about the changes between the old API and new API is stored and create a Comments object for it. The Comments object may be null if no file exists.

 // SingleComment[]

                                                
         
        // If validation is desired, write out the appropriate comments.xsd 
        // file in the same directory as the comments XML file.
        if (XMLToAPI.validateXML) {
            writeXSD(filename);
        }

        // If the file does not exist, return null
        File f = new File(filename);
        if (!f.exists())
            return null;

        // The instance of the Comments object which is populated from the file. 
        oldComments_ = new Comments();
        try {
            DefaultHandler handler = new CommentsHandler(oldComments_);
            XMLReader parser = null;
            try {
                String parserName = System.getProperty("org.xml.sax.driver");
                if (parserName == null) {
                    parser = org.xml.sax.helpers.XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
                } else {
                    // Let the underlying mechanisms try to work out which 
                    // class to instantiate
                    parser = org.xml.sax.helpers.XMLReaderFactory.createXMLReader();
                }
            } catch (SAXException saxe) {
                System.out.println("SAXException: " + saxe);
                saxe.printStackTrace();
                System.exit(1);
            }

            if (XMLToAPI.validateXML) {
                parser.setFeature("http://xml.org/sax/features/namespaces", true);
                parser.setFeature("http://xml.org/sax/features/validation", true);
                parser.setFeature("http://apache.org/xml/features/validation/schema", true);
            }
            parser.setContentHandler(handler);
            parser.setErrorHandler(handler);
            parser.parse(new InputSource(new FileInputStream(new File(filename))));
        } catch(org.xml.sax.SAXNotRecognizedException snre) {
            System.out.println("SAX Parser does not recognize feature: " + snre);
            snre.printStackTrace();
            System.exit(1);
        } catch(org.xml.sax.SAXNotSupportedException snse) {
            System.out.println("SAX Parser feature is not supported: " + snse);
            snse.printStackTrace();
            System.exit(1);
        } catch(org.xml.sax.SAXException saxe) {
            System.out.println("SAX Exception parsing file '" + filename + "' : " + saxe);
            saxe.printStackTrace();
            System.exit(1);
        } catch(java.io.IOException ioe) {
            System.out.println("IOException parsing file '" + filename + "' : " + ioe);
            ioe.printStackTrace();
            System.exit(1);
        }

        Collections.sort(oldComments_.commentsList_);
        return oldComments_;
    
public static booleanwriteFile(java.lang.String outputFileName, jdiff.Comments newComments)
Write the XML representation of comments to a file.

param
outputFileName The name of the comments file.
param
oldComments The old comments on the changed APIs.
param
newComments The new comments on the changed APIs.
return
true if no problems encountered

        try {
            FileOutputStream fos = new FileOutputStream(outputFileName);
            outputFile = new PrintWriter(fos);
            newComments.emitXMLHeader(outputFileName);
            newComments.emitComments();
            newComments.emitXMLFooter();
            outputFile.close();
        } catch(IOException e) {
            System.out.println("IO Error while attempting to create " + outputFileName);
            System.out.println("Error: "+ e.getMessage());
            System.exit(1);
        }
        return true;
    
public static voidwriteXSD(java.lang.String filename)
Write the XML Schema file used for validation.

        String xsdFileName = filename;
        int idx = xsdFileName.lastIndexOf('\\");
        int idx2 = xsdFileName.lastIndexOf('/");
        if (idx == -1 && idx2 == -1) {
            xsdFileName = "";
        } else if (idx == -1 && idx2 != -1) {
            xsdFileName = xsdFileName.substring(0, idx2+1);
        } else if (idx != -1  && idx2 == -1) {
            xsdFileName = xsdFileName.substring(0, idx+1);
        } else if (idx != -1  && idx2 != -1) {
            int max = idx2 > idx ? idx2 : idx;
            xsdFileName = xsdFileName.substring(0, max+1);
        }
        xsdFileName += "comments.xsd";
        try {
            FileOutputStream fos = new FileOutputStream(xsdFileName);
            PrintWriter xsdFile = new PrintWriter(fos);
            // The contents of the comments.xsd file
            xsdFile.println("<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"no\"?>");
            xsdFile.println("<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">");
            xsdFile.println();
            xsdFile.println("<xsd:annotation>");
            xsdFile.println("  <xsd:documentation>");
            xsdFile.println("  Schema for JDiff comments.");
            xsdFile.println("  </xsd:documentation>");
            xsdFile.println("</xsd:annotation>");
            xsdFile.println();
            xsdFile.println("<xsd:element name=\"comments\" type=\"commentsType\"/>");
            xsdFile.println();
            xsdFile.println("<xsd:complexType name=\"commentsType\">");
            xsdFile.println("  <xsd:sequence>");
            xsdFile.println("    <xsd:element name=\"comment\" type=\"commentType\" minOccurs='0' maxOccurs='unbounded'/>");
            xsdFile.println("  </xsd:sequence>");
            xsdFile.println("  <xsd:attribute name=\"name\" type=\"xsd:string\"/>");
            xsdFile.println("  <xsd:attribute name=\"jdversion\" type=\"xsd:string\"/>");
            xsdFile.println("</xsd:complexType>");
            xsdFile.println();
            xsdFile.println("<xsd:complexType name=\"commentType\">");
            xsdFile.println("  <xsd:sequence>");
            xsdFile.println("    <xsd:element name=\"identifier\" type=\"identifierType\" minOccurs='1' maxOccurs='unbounded'/>");
            xsdFile.println("    <xsd:element name=\"text\" type=\"xsd:string\" minOccurs='1' maxOccurs='1'/>");
            xsdFile.println("  </xsd:sequence>");
            xsdFile.println("</xsd:complexType>");
            xsdFile.println();
            xsdFile.println("<xsd:complexType name=\"identifierType\">");
            xsdFile.println("  <xsd:attribute name=\"id\" type=\"xsd:string\"/>");
            xsdFile.println("</xsd:complexType>");
            xsdFile.println();
            xsdFile.println("</xsd:schema>");
            xsdFile.close();
        } catch(IOException e) {
            System.out.println("IO Error while attempting to create " + xsdFileName);
            System.out.println("Error: " +  e.getMessage());
            System.exit(1);
        }