Skip to content

Commit bc4d41b

Browse files
committed
faster filename comparator
1 parent 77207ba commit bc4d41b

1 file changed

Lines changed: 52 additions & 39 deletions

File tree

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,83 @@
11
/**
22
* This file is part of the Goobi Application - a Workflow tool for the support of mass digitization.
3-
*
3+
*
44
* Visit the websites for more information.
55
* - https://goobi.io
66
* - https://www.intranda.com
77
* - https://github.com/intranda/goobi-workflow
8-
*
8+
*
99
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
1010
* Software Foundation; either version 2 of the License, or (at your option) any later version.
11-
*
11+
*
1212
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1313
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14-
*
14+
*
1515
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59
1616
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
1717
*/
1818
package de.sub.goobi.helper;
1919

2020
import java.util.Comparator;
21+
import java.util.regex.Pattern;
2122

2223
public class GoobiStringFileComparator implements Comparator<String> {
23-
private static final String DIGIT_NON_DIGIT_SPLIT_REGEX = "(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)";
24+
25+
// Split at every boundary between a digit and a non-digit, so alphanumeric and numeric parts can be compared separately.
26+
private static final Pattern DIGIT_NON_DIGIT_SPLIT = Pattern.compile("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
2427

2528
@Override
2629
public int compare(String s1, String s2) {
27-
// Ignore file extension
28-
if (s1.contains(".")) {
29-
s1 = s1.substring(0, s1.lastIndexOf(".")).toLowerCase();
30-
}
31-
if (s2.contains(".")) {
32-
s2 = s2.substring(0, s2.lastIndexOf(".")).toLowerCase();
30+
String[] a = split(s1);
31+
String[] b = split(s2);
32+
33+
int len = Math.min(a.length, b.length);
34+
for (int i = 0; i < len; i++) {
35+
int result = compareComponent(a[i], b[i]);
36+
if (result != 0) {
37+
return result;
38+
}
3339
}
3440

35-
String[] s1Components = s1.split(DIGIT_NON_DIGIT_SPLIT_REGEX);
36-
String[] s2Components = s2.split(DIGIT_NON_DIGIT_SPLIT_REGEX);
41+
// all shared components are equal, so the shorter name wins
42+
return Integer.compare(a.length, b.length);
43+
}
3744

38-
int componentIndex = 0;
39-
while (componentIndex < s1Components.length && componentIndex < s2Components.length) {
40-
String a = s1Components[componentIndex];
41-
String b = s2Components[componentIndex];
42-
try {
43-
Integer i1 = Integer.parseInt(a);
44-
Integer i2 = Integer.parseInt(b);
45-
int result = i1 - i2;
46-
if (result != 0) {
47-
return result;
48-
}
49-
} catch (NumberFormatException e) {
50-
int result = a.compareTo(b);
51-
if (result != 0) {
52-
return result;
53-
}
54-
}
55-
componentIndex++;
45+
private static String[] split(String name) {
46+
// ignore the file extension
47+
int dot = name.lastIndexOf('.');
48+
if (dot >= 0) {
49+
name = name.substring(0, dot);
5650
}
51+
return DIGIT_NON_DIGIT_SPLIT.split(name.toLowerCase());
52+
}
5753

58-
// All components were compared
59-
if (s1Components.length == s2Components.length) {
60-
return 0;
54+
private static int compareComponent(String a, String b) {
55+
// the split guarantees each component is either all digits or all non-digits
56+
boolean aNum = !a.isEmpty() && Character.isDigit(a.charAt(0));
57+
boolean bNum = !b.isEmpty() && Character.isDigit(b.charAt(0));
58+
if (aNum && bNum) {
59+
return compareNumeric(a, b);
6160
}
61+
return a.compareTo(b);
62+
}
63+
64+
// Compares two pure digit sequences numerically, without parsing (avoids overflow for very large numbers).
65+
private static int compareNumeric(String a, String b) {
66+
int ai = skipLeadingZeros(a);
67+
int bi = skipLeadingZeros(b);
68+
int aLen = a.length() - ai;
69+
int bLen = b.length() - bi;
70+
if (aLen != bLen) {
71+
return Integer.compare(aLen, bLen);
72+
}
73+
return a.substring(ai).compareTo(b.substring(bi));
74+
}
6275

63-
// otherwise, the shorter filename wins
64-
if (s1Components.length < s2Components.length) {
65-
return -1;
66-
} else {
67-
return 1;
76+
private static int skipLeadingZeros(String s) {
77+
int i = 0;
78+
while (i < s.length() - 1 && s.charAt(i) == '0') {
79+
i++;
6880
}
81+
return i;
6982
}
7083
}

0 commit comments

Comments
 (0)