PDF-Import: java.lang.IllegalArgumentException: Comparison method violates its general contract!

Beim Versuch, PDFs zu importieren, erhalte ich bei einem Ordner mit Daten aus 2022 unmittelbar eine Fehlermeldung:

Error log:

Tue Dec 06 13:20:22 CST 2022
Internal Error
java.lang.IllegalArgumentException: Comparison method violates its general contract!
	at java.base/java.util.TimSort.mergeLo(Unknown Source)
	at java.base/java.util.TimSort.mergeAt(Unknown Source)
	at java.base/java.util.TimSort.mergeCollapse(Unknown Source)
	at java.base/java.util.TimSort.sort(Unknown Source)
	at java.base/java.util.Arrays.sort(Unknown Source)
	at java.base/java.util.ArrayList.sort(Unknown Source)
	at name.abuchen.portfolio.ui.handlers.ImportPDFHandler.runImportWithFiles(ImportPDFHandler.java:158)
	at name.abuchen.portfolio.ui.handlers.ImportPDFHandler.runImport(ImportPDFHandler.java:136)
	at name.abuchen.portfolio.ui.views.PortfolioListView.lambda$7(PortfolioListView.java:302)
	at name.abuchen.portfolio.ui.util.SimpleAction.run(SimpleAction.java:69)
	at org.eclipse.jface.action.Action.runWithEvent(Action.java:474)
	at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:580)
	at org.eclipse.jface.action.ActionContributionItem.lambda$4(ActionContributionItem.java:414)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:89)
	at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4243)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1063)
	at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4060)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3632)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1155)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:338)
	at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1046)
	at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:155)
	at org.eclipse.e4.ui.internal.workbench.swt.E4Application.start(E4Application.java:168)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:203)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:136)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:401)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:255)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.base/java.lang.reflect.Method.invoke(Unknown Source)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:659)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:596)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1467)


Der Fehler scheint also aufzutreten an dieser Stelle:

public static void runImportWithFiles(PortfolioPart part, Shell shell, Client client, Account account,
                    Portfolio portfolio, List<File> files)
    {
        files.sort((File lhs, File rhs) -> {
            int modDiff = (int) (lhs.lastModified() - rhs.lastModified());
            return modDiff == 0 ? lhs.getPath().compareTo(rhs.getPath()) : modDiff;
        });

   //...

Jetzt habe ich bei mir diesen Codeblock lokal laufen lassen für den betroffenen Ordner:

List<File> files = Arrays.asList(new File("mydir").listFiles());

files.sort((File lhs, File rhs) -> {
	int modDiff = (int) (lhs.lastModified() - rhs.lastModified());
	return modDiff == 0 ? lhs.getPath().compareTo(rhs.getPath()) : modDiff;
});

Ergebnis: Die gleiche Exception tritt auf.

Der Grund scheint in einem Integer-Overflow zu liegen. Bei mir im Beispiel:

image

Der Sort-Aufruf sollte aus meiner Sicht wie folgt angepasst werden:

files.sort((File lhs, File rhs) -> {
	int modDiff = Long.compare(lhs.lastModified(), rhs.lastModified());
	return modDiff == 0 ? lhs.getPath().compareTo(rhs.getPath()) : modDiff;
});

oder:

files.sort(Comparator.comparing(File::lastModified).thenComparing(File::getPath));

4 Likes

Danke für die Analyse. Der Cast auf Integer scheint mir das Problem zu sein. Magst Du eine PR auf Github stellen? Ansonsten: bekommst Du einen lokale Version gebaut um weiterzuarbeiten?

1 Like

Danke für die schnelle Rückmeldung!

Da muss ich die Tage schauen, ob ich mir die Umgebung mal lokal einrichten kann. Falls auch noch andere das Problem haben, wäre ein PR durch jemand anderen also ggf. sinnvoller.

Wenn du nicht unbedingt einen PR anlegen willst und es für @AndreasB einfacher ist (als die Änderung selbst an die Stelle zu bringen), wenn ein PR da ist, dann kann ich das gerne machen. Du hast ja quasi schon alles herausgearbeitet und es muss nur noch geändert werden. :+1:t4:
Wenn du aber generell Lust und Laune hast Ideen/Fixes als Code beizutragen, dann könnte das Anlegen des PRs ein guter erster Einstieg für dich sein.

EDIT 10.12.22: Andreas hat die Änderung schon vorgenommen:

2 Likes