#39 StringIndexOutOfBoundsException with 2+ parameterized bounds

open
nobody
None
5
2011-09-08
2011-09-08
Anonymous
No

That is, it's a problem to have <T extends Foo<?> & Bar<?>>. Here's an example of how to reproduce the problem:

$ export CP=... # jdiff built from head, plus xerces

$ mkdir p

$ echo 'package p; public interface Foo {}' > p/Foo.java

$ javadoc -docletpath $CP -doclet jdiff.JDiff -apidir . -apiname old p
Loading source files for package p...
Constructing Javadoc information...
JDiff: doclet started ...
JDiff: writing the API to file './old.xml'...
JDiff: finished (took 0s, not including scanning the source files).

$ echo 'package p; public interface Foo { <T extends Enum<T> & Iterable<T>> void foo(T t); }' > p/Foo.java

$ javadoc -docletpath $CP -doclet jdiff.JDiff -apidir . -apiname new p
Loading source files for package p...
Constructing Javadoc information...
JDiff: doclet started ...
JDiff: writing the API to file './new.xml'...
JDiff: finished (took 0s, not including scanning the source files).

$ javadoc -docletpath $CP -doclet jdiff.JDiff -oldapidir . -newapidir . -oldapi old -newapi new p
Loading source files for package p...
Constructing Javadoc information...
JDiff: doclet started ...
JDiff: reading the old API in from file './old.xml'... finished
JDiff: reading the new API in from file './new.xml'... finished
JDiff: comparing the old and new APIs ...
Approximately 100% difference between the APIs
JDiff: reading the comments in from file 'user_comments_for_old_to_new.xml'...
(the comments file will be created)
JDiff: generating HTML report into the file 'changes.html' and the subdirectory 'changes'
javadoc: error - In doclet class jdiff.JDiff, method start has thrown an exception java.lang.reflect.InvocationTargetException
java.lang.StringIndexOutOfBoundsException: String index out of range: -23
at java.lang.String.substring(String.java:1949)
at jdiff.HTMLReportGenerator.simpleName(HTMLReportGenerator.java:1825)
at jdiff.HTMLReportGenerator.simpleName(HTMLReportGenerator.java:1825)
at jdiff.HTMLReportGenerator.writeMethodTableEntry(HTMLReportGenerator.java:1264)
at jdiff.HTMLReportGenerator.reportAllMethods(HTMLReportGenerator.java:583)
at jdiff.HTMLReportGenerator.reportChangedClass(HTMLReportGenerator.java:492)
at jdiff.HTMLReportGenerator.reportChangedPackage(HTMLReportGenerator.java:384)
at jdiff.HTMLReportGenerator.writeReport(HTMLReportGenerator.java:214)
at jdiff.HTMLReportGenerator.generate(HTMLReportGenerator.java:106)
at jdiff.JDiff.startGeneration(JDiff.java:116)
at jdiff.JDiff.start(JDiff.java:32)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at com.sun.tools.javadoc.DocletInvoker.invoke(DocletInvoker.java:283)
at com.sun.tools.javadoc.DocletInvoker.start(DocletInvoker.java:163)
at com.sun.tools.javadoc.Start.parseAndExecute(Start.java:397)
at com.sun.tools.javadoc.Start.begin(Start.java:167)
at com.sun.tools.javadoc.Main.execute(Main.java:59)
at com.sun.tools.javadoc.Main.main(Main.java:49)
1 error

Discussion

  • Comment has been marked as spam. 
    Undo

    You can see all pending comments posted by this user  here

    Anonymous

    Anonymous - 2011-09-14

    Note that, even after a hacky fix, we see problems on subsequent jdiff invocations because the user_comments file contains unescaped & characters:

    JDiff: reading the comments in from file 'xyz/user_comments_for_xyz.xml'...
    Fatal Error (709): parsing XML comments file:org.xml.sax.SAXParseException; lineNumber: 709; columnNumber: 96; The entity name must immediately follow the '&' in the entity reference.
    org.xml.sax.SAXParseException; lineNumber: 709; columnNumber: 96; The entity name must immediately follow the '&' in the entity reference.
    at org.apache.xerces.util.ErrorHandlerWrapper.createSAXParseException(Unknown Source)
    at org.apache.xerces.util.ErrorHandlerWrapper.fatalError(Unknown Source)
    at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
    at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
    at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
    at org.apache.xerces.impl.XMLScanner.reportFatalError(Unknown Source)
    at org.apache.xerces.impl.XMLScanner.scanAttributeValue(Unknown Source)
    at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanAttribute(Unknown Source)
    at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanStartElement(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
    at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
    at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
    at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
    at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
    at jdiff.Comments.readFile(Comments.java:84)
    at jdiff.JDiff.startGeneration(JDiff.java:110)
    at jdiff.JDiff.start(JDiff.java:32)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:616)
    at com.sun.tools.javadoc.DocletInvoker.invoke(DocletInvoker.java:283)
    at com.sun.tools.javadoc.DocletInvoker.start(DocletInvoker.java:163)
    at com.sun.tools.javadoc.Start.parseAndExecute(Start.java:397)
    at com.sun.tools.javadoc.Start.begin(Start.java:167)
    at com.sun.tools.javadoc.Main.execute(Main.java:59)
    at com.sun.tools.javadoc.Main.main(Main.java:49)

    The problematic method is the same as our original problematic method. Line 709 is:

    <identifier id="com.google.common.collect.Maps.uniqueIndex_added(I extends java.lang.Object & java.lang.Iterable&lt;V&gt; & java.util.Iterator&lt;V&gt;, com.google.common.base.Function&lt;? super V, K&gt;)"/>

     
  • Comment has been marked as spam. 
    Undo

    You can see all pending comments posted by this user  here

    Anonymous

    Anonymous - 2011-09-15

    We believe we have a fix for the first problem, which we'd be happy to contribute (though I'm told that Google retains the copyright in these things, and I can ask further questions of our open-source team if it would be helpful).

    --- src/jdiff/HTMLReportGenerator.java.orig 2011-09-07 18:09:29.884666000 -0400
    +++ src/jdiff/HTMLReportGenerator.java 2011-09-09 12:23:10.261526000 -0400
    @@ -1788,54 +1788,11 @@
    public static String simpleName(String fqNames) {
    if (fqNames == null)
    return null;
    - String res = "";
    - boolean hasContent = false;
    - // We parse the string step by step to ensure we take
    - // fqNames that contains generics parameter in a whole.
    - ArrayList<String> fqNamesList = new ArrayList<String>();
    - int genericParametersDepth = 0;
    - StringBuffer buffer = new StringBuffer();
    - for (int i=0; i<fqNames.length(); i++) {
    - char c = fqNames.charAt(i);
    - if ('<' == c) {
    - genericParametersDepth++;
    - }
    - if ('>' == c) {
    - genericParametersDepth--;
    - }
    - if (',' != c || genericParametersDepth > 0) {
    - buffer.append(c);
    - } else if (',' == c) {
    - fqNamesList.add(buffer.toString().trim());
    - buffer = new StringBuffer(buffer.length());
    - }
    - }
    - fqNamesList.add(buffer.toString().trim());
    - for (String fqName : fqNamesList) {
    - // Assume this will be used inside a <nobr> </nobr> set of tags.
    - if (hasContent)
    - res += ", ";
    - hasContent = true;
    - // Look for text within '<' and '>' in case this is a invocation of a generic
    -
    - int firstBracket = fqName.indexOf('<');
    - int lastBracket = fqName.lastIndexOf('>');
    - String genericParameter = null;
    - if (firstBracket != -1 && lastBracket != -1) {
    - genericParameter = simpleName(fqName.substring(firstBracket + 1, lastBracket));
    - fqName = fqName.substring(0, firstBracket);
    - }
    -
    - int lastDot = fqName.lastIndexOf('.');
    - if (lastDot < 0) {
    - res += fqName; // Already as simple as possible
    - } else {
    - res += fqName.substring(lastDot+1);
    - }
    - if (genericParameter != null)
    - res += "&lt;" + genericParameter + "&gt;";
    - }
    - return res;
    + return fqNames
    + .replaceAll("[\\w$.]+[.]([\\w$]+)", "$1")
    + .replaceAll("&", "&amp;")
    + .replaceAll("<", "&lt;")
    + .replaceAll(">", "&gt;");
    }

    /**

     
  • Matthew Doar

    Matthew Doar - 2011-09-15

    Chris,

    Thanks for the bug and nicely compact test cases. I'd happy to put the fix in and Google is welcome to the copyright (for just the changes, right?). The JDiff tool continues to be useful, if a little awkward to use. I haven't worked on it in some years though.

    ~Matt

     
  • Comment has been marked as spam. 
    Undo

    You can see all pending comments posted by this user  here

    Anonymous

    Anonymous - 2011-09-19

    Yep, just for the changes. Thanks!

     

Log in to post a comment.