Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 460484

Summary: ImportRewrite throws SIOOBE when trying to add import
Product: [Eclipse Project] JDT Reporter: Markus Keller <markus.kell.r>
Component: CoreAssignee: John Glassmyer <eclipse>
Status: VERIFIED FIXED QA Contact:
Severity: normal    
Priority: P3 CC: eclipse, jarthana, manoj.palat, shankhba
Version: 4.5   
Target Milestone: 4.5 M6   
Hardware: PC   
OS: Windows 7   
See Also: https://git.eclipse.org/r/42468
https://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=1490ec2d61fb8e59c378e50775f4749531490d5f
Whiteboard:
Bug Depends on: 430303    
Bug Blocks:    

Description Markus Keller CLA 2015-02-20 16:54:29 EST
follow-up to bug 430303

The slightly strange behavior that has been swept under the rug by modifying org.eclipse.jdt.core.tests.rewrite.describing.ImportRewriteTest#testAddImports1()
left me a bit uneasy.

On addImport, when the existing imports were properly grouped but not properly sorted within an import group, I would not have expected the group to be split into two.

When I tried to reproduce the test case in master, I found the following bug:

- have a project with default Organize Imports preferences
- create class p.A
- create this CU:

package pack1;

import java.util.Set;
import java.util.Vector;
import java.util.Map;

import pack.List;
import pack.List2;

public class C {
    java.net.Socket s;
    p.A a;
    com.sun.beans.TypeResolver t;
    
    Object[] coll = { Set.class, Vector.class, Map.class };
}

- select "p.A" in C.java and execute Source > Add Import
- result:

!ENTRY org.eclipse.jdt.ui 4 10001 2015-02-20 22:35:19.336
!MESSAGE Internal Error
!STACK 0
java.lang.reflect.InvocationTargetException
	at [..]

Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: 1
	at java.lang.String.charAt(String.java:646)
	at org.eclipse.jdt.internal.core.dom.rewrite.imports.OrderPreservingImportAdder.countMatchingPrefixSegments(OrderPreservingImportAdder.java:62)
	at org.eclipse.jdt.internal.core.dom.rewrite.imports.OrderPreservingImportAdder.shouldGroupWithSucceeding(OrderPreservingImportAdder.java:170)
	at org.eclipse.jdt.internal.core.dom.rewrite.imports.OrderPreservingImportAdder.determineAdjacentNewImports(OrderPreservingImportAdder.java:142)
	at org.eclipse.jdt.internal.core.dom.rewrite.imports.OrderPreservingImportAdder.addImports(OrderPreservingImportAdder.java:91)
	at org.eclipse.jdt.internal.core.dom.rewrite.imports.ImportRewriteAnalyzer.computeImportOrder(ImportRewriteAnalyzer.java:588)
	at org.eclipse.jdt.internal.core.dom.rewrite.imports.ImportRewriteAnalyzer.analyzeRewrite(ImportRewriteAnalyzer.java:540)
	at org.eclipse.jdt.core.dom.rewrite.ImportRewrite.rewriteImports(ImportRewrite.java:1178)
	at org.eclipse.jdt.internal.corext.codemanipulation.AddImportsOperation.run(AddImportsOperation.java:182)
	at org.eclipse.jdt.internal.core.BatchOperation.executeOperation(BatchOperation.java:39)
	at org.eclipse.jdt.internal.core.JavaModelOperation.run(JavaModelOperation.java:729)
	at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2313)
	at org.eclipse.jdt.core.JavaCore.run(JavaCore.java:5409)
	at org.eclipse.jdt.internal.ui.actions.WorkbenchRunnableAdapter.run(WorkbenchRunnableAdapter.java:106)
	at org.eclipse.jface.operation.ModalContext.runInCurrentThread(ModalContext.java:463)
	... 45 more
Root exception:
java.lang.StringIndexOutOfBoundsException: String index out of range: 1
	at java.lang.String.charAt(String.java:646)
	at org.eclipse.jdt.internal.core.dom.rewrite.imports.OrderPreservingImportAdder.countMatchingPrefixSegments(OrderPreservingImportAdder.java:62)
	at org.eclipse.jdt.internal.core.dom.rewrite.imports.OrderPreservingImportAdder.shouldGroupWithSucceeding(OrderPreservingImportAdder.java:170)
	at org.eclipse.jdt.internal.core.dom.rewrite.imports.OrderPreservingImportAdder.determineAdjacentNewImports(OrderPreservingImportAdder.java:142)
	at org.eclipse.jdt.internal.core.dom.rewrite.imports.OrderPreservingImportAdder.addImports(OrderPreservingImportAdder.java:91)
	at org.eclipse.jdt.internal.core.dom.rewrite.imports.ImportRewriteAnalyzer.computeImportOrder(ImportRewriteAnalyzer.java:588)
	at org.eclipse.jdt.internal.core.dom.rewrite.imports.ImportRewriteAnalyzer.analyzeRewrite(ImportRewriteAnalyzer.java:540)
	at org.eclipse.jdt.core.dom.rewrite.ImportRewrite.rewriteImports(ImportRewrite.java:1178)
	at org.eclipse.jdt.internal.corext.codemanipulation.AddImportsOperation.run(AddImportsOperation.java:182)
	at org.eclipse.jdt.internal.core.BatchOperation.executeOperation(BatchOperation.java:39)
	at org.eclipse.jdt.internal.core.JavaModelOperation.run(JavaModelOperation.java:729)
	at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2313)
	at org.eclipse.jdt.core.JavaCore.run(JavaCore.java:5409)
	at org.eclipse.jdt.internal.ui.actions.WorkbenchRunnableAdapter.run(WorkbenchRunnableAdapter.java:106)
	at org.eclipse.jface.operation.ModalContext.runInCurrentThread(ModalContext.java:463)
	at org.eclipse.jface.operation.ModalContext.run(ModalContext.java:371)
	at org.eclipse.ui.internal.WorkbenchWindow$13.run(WorkbenchWindow.java:2138)
	at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70)
	at org.eclipse.ui.internal.WorkbenchWindow.run(WorkbenchWindow.java:2134)
	at org.eclipse.ui.internal.progress.ProgressManager$RunnableWithStatus.run(ProgressManager.java:1394)
	at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70)
	at org.eclipse.ui.internal.progress.ProgressManager$5.run(ProgressManager.java:1228)
	at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:187)
	at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:145)
	at org.eclipse.swt.widgets.Display.syncExec(Display.java:4753)
	at org.eclipse.ui.internal.progress.ProgressManager.runInUI(ProgressManager.java:1225)
	at org.eclipse.jdt.internal.ui.javaeditor.AddImportOnSelectionAction.run(AddImportOnSelectionAction.java:138)
	at org.eclipse.jface.action.Action.runWithEvent(Action.java:473)
	at org.eclipse.ui.actions.RetargetAction.runWithEvent(RetargetAction.java:239)
	at org.eclipse.ui.internal.WWinPluginAction.runWithEvent(WWinPluginAction.java:233)
	at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:595)
	at org.eclipse.jface.action.ActionContributionItem.access$2(ActionContributionItem.java:511)
	at org.eclipse.jface.action.ActionContributionItem$5.handleEvent(ActionContributionItem.java:420)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4354)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1061)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4172)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3761)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$9.run(PartRenderingEngine.java:1151)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:337)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1032)
	at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:156)
	at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:651)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:337)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:595)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:150)
	at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:138)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:380)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:235)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:648)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:603)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1465)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1438)
Comment 1 John Glassmyer CLA 2015-02-20 17:00:14 EST
Thanks, Markus! I'll look into it.
Comment 2 Markus Keller CLA 2015-02-20 17:00:54 EST
I've worked around the SIOOBE with http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=1798df591429fc6a5448f8a074f2d563846bcebb

John, how hard would it be to not split up an import group if it's not properly sorted? If doable, then that would be the best solution.

Depending on the final result, we should either revert some of the test changes that were done in bug 430303, or at least add a test for the SIOOBE.
Comment 3 Eclipse Genie CLA 2015-02-23 15:40:13 EST
New Gerrit change created: https://git.eclipse.org/r/42468
Comment 4 John Glassmyer CLA 2015-02-23 15:53:37 EST
The cause of the exception was not the order of imports within groups per se, but rather incorrect logic used when comparing import names. Specifically, adding an import that would correctly sort between two existing imports, where the first differing package name segments (of the new import and of an adjacent existing import) shared a common prefix, but were of different lengths, would throw an exception.

I have added a minimal test case addressing this at https://git.eclipse.org/r/42468.

In hindsight, I vaguely remember running into this case during development of bug 430303, but sadly I neglected to add a test case and come back and fix the cause. I'm glad you investigated, Markus!

As for your other question, I do not believe that it would be possible to maintain groupings of incorrectly ordered existing imports without introducing unintended bugs of the sort that I initially reported in bug 430303. The old ImportRewriteAnalyzer attempted to do some things of this sort which seemed superficially like nice things to do but which did not add up to a logically consistent whole, and this resulted in the inconsistencies reported in 430303.
Comment 6 Markus Keller CLA 2015-03-05 14:52:59 EST
Thanks, pushed the test case.

I agree that there's no consistent solution for cases like bug 430303 comment 0.

However, the case in ImportRewriteTest#testAddImports1() / comment 0 is a bit different. Here, it's not about adding an import that doesn't match any group, or about starting with a completely unsorted import section.

Here, it's about a situation where imports are already properly grouped, and it would be clear to which group the new import belongs. In comment 0, the new import "p.A" belongs to the "others" group at the end of the imports section. The only part that's wrong in the initial import section is that the entries within a group are not properly sorted.

Finding the "right" position in an unsorted group is of course not possible, so all I'd expect is that the import would be added anywhere in that group (e.g. at the end).

OTOH, this is probably just an academic exercise, since this situation only arises when users never use Organize Imports again. So let's see if we get bugs from "real" users.
Comment 7 shankha banerjee CLA 2015-03-17 04:42:17 EDT
Verified for 4.5 M6 with build I20150316-2000.