1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.cosylab.util;
21
22 import java.util.Arrays;
23 import java.util.Comparator;
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130 public class NumericStringComparator implements Comparator
131 {
132
133 public static final int DEC_SEPARATOR_DOT = 1;
134
135
136 public static final int DEC_SEPARATOR_COMMA = 2;
137
138
139
140
141
142 public static final int DEC_SEPARATOR_BOTH = 3;
143
144
145
146
147
148 public static final int DEC_SEPARATOR_NONE = 4;
149 private static final char COMMA = ',';
150 private static final char DOT = '.';
151 private static NumericStringComparator defaultInstance;
152
153
154
155
156
157
158
159
160
161 public static NumericStringComparator getDefault()
162 {
163 if (defaultInstance == null) {
164 defaultInstance = new NumericStringComparator();
165 }
166
167 return defaultInstance;
168 }
169
170 private boolean softEquals = false;
171
172
173 private char[] separators;
174
175
176 private int decSeparator;
177
178
179
180
181
182
183
184
185
186
187 public NumericStringComparator(char[] separators, int decSeparator)
188 {
189 setSeparators(separators);
190 this.decSeparator = decSeparator;
191 }
192
193
194
195
196
197
198
199 public NumericStringComparator(char[] separators)
200 {
201 this(separators, NumericStringComparator.DEC_SEPARATOR_DOT);
202 }
203
204
205
206
207
208
209
210 public NumericStringComparator(int decSeparator)
211 {
212 this(new char[]{ ' ', '_' }, decSeparator);
213 }
214
215
216
217
218
219
220 public NumericStringComparator()
221 {
222 this(NumericStringComparator.DEC_SEPARATOR_DOT);
223 }
224
225
226
227
228
229
230
231
232
233
234 public void addSeparator(char sep)
235 {
236 if (Arrays.binarySearch(this.separators, sep) >= 0) {
237 return;
238 }
239
240 if (sep == '-') {
241 throw new IllegalArgumentException(
242 "Minus sign should not be used as a separator.");
243 }
244
245 if (isDecSeparator(sep)) {
246 throw new IllegalArgumentException("Character '" + sep
247 + "' should not be used as a separator. It is defined as a decimal separator.");
248 }
249
250 if (Character.isDigit(sep)) {
251 throw new IllegalArgumentException(
252 "A digit should not be used as a separator.");
253 }
254
255 char[] temp = new char[separators.length + 1];
256 System.arraycopy(separators, 0, temp, 0, separators.length);
257 temp[temp.length - 1] = sep;
258 Arrays.sort(temp);
259 this.separators = temp;
260 }
261
262
263
264
265
266
267
268
269
270
271 public void setSeparators(char[] sep)
272 {
273 this.separators = new char[sep.length];
274 System.arraycopy(sep, 0, separators, 0, sep.length);
275 Arrays.sort(this.separators);
276
277 for (int i = 0; i < sep.length; i++) {
278 if (isDecSeparator(sep[i])) {
279 throw new IllegalArgumentException("Cannot set " + sep[i]
280 + " as separator. It is already set as decimal separator.");
281 }
282
283 if (Character.isDigit(sep[i])) {
284 throw new IllegalArgumentException("Cannot set " + sep[i]
285 + " as separator. It is a digit.");
286 }
287
288 if (sep[i] == '-') {
289 throw new IllegalArgumentException("Cannot set " + sep[i]
290 + " as separator. It is used as a negative sign.");
291 }
292 }
293 }
294
295
296
297
298
299
300 public char[] getSeparators()
301 {
302 return this.separators;
303 }
304
305
306
307
308
309
310 public int getDecSeparatorMode()
311 {
312 return this.decSeparator;
313 }
314
315
316
317
318
319
320
321
322
323 private boolean isSeparatorOrDigit(char ch)
324 {
325 return (Character.isDigit(ch) || isDecSeparator(ch) || isSeparator(ch));
326 }
327
328
329
330
331
332
333
334
335 private final boolean isSeparator(char ch)
336 {
337 int idx = separators.length - 1;
338
339 while (idx >= 0) {
340 if (separators[idx--] == ch) {
341 return true;
342 }
343 }
344
345 return false;
346 }
347
348
349
350
351
352
353
354
355
356 private final boolean isDecSeparator(final char ch)
357 {
358 switch (this.decSeparator) {
359 case DEC_SEPARATOR_NONE:
360 return false;
361
362 case DEC_SEPARATOR_BOTH:
363 return (ch == DOT || ch == COMMA);
364
365 case DEC_SEPARATOR_COMMA:
366 return ch == COMMA;
367
368 case DEC_SEPARATOR_DOT:
369 return ch == DOT;
370 }
371
372 return false;
373 }
374
375
376
377
378 public int compare(Object o1, Object o2)
379 {
380 String s1 = o1.toString();
381 String s2 = o2.toString();
382 if (softEquals) {
383 s1=s1.trim();
384 s2=s2.trim();
385 }
386 final int[] idx = { 0, 0 };
387 final int[] len = { s1.length(), s2.length() };
388
389 if ((len[0] == 0) || (len[1] == 0)) {
390 return s1.compareTo(s2);
391 }
392
393 int[] ret = {0, 0, 1};
394
395 while (true) {
396 if (idx[0] >= len[0]) {
397 if (softEquals) {
398 return ret[0];
399 }
400 if (ret[0]!=0) {
401 return ret[0];
402 }
403 return (ret[1] == 0) ? (idx[1] - len[1])*ret[2] : ret[1];
404 } else if (idx[1] >= len[1]) {
405 if (softEquals) {
406 return ret[0];
407 }
408 return (ret[1] == 0) ? (len[0] - idx[0])*ret[2] : ret[1];
409 }
410
411 if (processItem(s1, s2, idx, len, ret)) {
412 return ret[0];
413 }
414 }
415 }
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436 private final boolean processItem(String s1, String s2, int[] idx,
437 int[] len, int[] ret)
438 {
439 char c1;
440 char c2;
441 int nComp = 0;
442 boolean decFound = false;
443 boolean numFound = false;
444 boolean sComp = softEquals?false:(ret[1] == 0);
445
446 c1 = s1.charAt(idx[0]++);
447 c2 = s2.charAt(idx[1]++);
448
449
450 while (isSeparator(c1) || c1 == '0') {
451 if (sComp) {
452
453 if (c1 != c2) {
454 ret[1] = c1 - c2;
455 sComp = false;
456 } else {
457 if (idx[1] == len[1]) {
458 return false;
459 }
460
461 c2 = s2.charAt(idx[1]++);
462 }
463 }
464
465 if (idx[0] == len[0]) {
466 if (softEquals && c1 == '0') {
467 break;
468 } else {
469 ret[0]=Character.isDigit(c2)?-1:ret[1];
470 return false;
471 }
472 }
473
474 c1 = s1.charAt(idx[0]++);
475 }
476
477
478 while (isSeparator(c2) || c2 == '0') {
479 if (sComp) {
480
481 if (c1 != c2) {
482 ret[1] = c1 - c2;
483 sComp = false;
484 } else {
485 if (idx[0] == len[0]) {
486 return false;
487 }
488
489 c1 = s1.charAt(idx[0]++);
490 }
491 }
492
493 if (idx[1] == len[1]) {
494 if (softEquals && c2 == '0') {
495 break;
496 } else {
497 return false;
498 }
499 }
500
501 c2 = s2.charAt(idx[1]++);
502 }
503
504 boolean minus1 = false;
505
506 if (c1 == '-') {
507 minus1 = true;
508
509 if (idx[0] == len[0]) {
510 return false;
511 }
512
513 c1 = s1.charAt(idx[0]++);
514 }
515
516 boolean minus2 = false;
517
518 if (c2 == '-') {
519 minus2 = true;
520
521 if (idx[1] == len[1]) {
522 return false;
523 }
524
525 c2 = s2.charAt(idx[1]++);
526 }
527
528 if (minus1 ^ minus2) {
529 if ((isDecSeparator(c1) || Character.isDigit(c1))
530 && (isDecSeparator(c2) || Character.isDigit(c2))) {
531 ret[0] = minus1 ? -1 : 1;
532
533 return true;
534 } else if (sComp) {
535
536 if (minus1) {
537 ret[0] = '-' - c2;
538 } else {
539 ret[0] = c1 - '-';
540 }
541
542 return true;
543 }
544 } else {
545 ret[2] = minus1?-1:1;
546 }
547
548
549 while (Character.isDigit(c1)) {
550 if (Character.isDigit(c2)) {
551 numFound = true;
552
553 if (c1 != c2) {
554 if (sComp) {
555 ret[1] = c1 - c2;
556 sComp = false;
557 }
558
559 if (nComp == 0) {
560 nComp = c1 - c2;
561 }
562 }
563 } else {
564 if (numFound) {
565 ret[0] = 1;
566 } else {
567 ret[0] = c1 - c2;
568 }
569
570 if (minus1 && numFound) {
571 ret[0] = -ret[0];
572 }
573
574 return ret[0] != 0;
575 }
576
577 if (idx[0] >= len[0]) {
578 if (idx[1] == len[1] || !Character.isDigit(s2.charAt(idx[1]))) {
579 ret[0] = nComp;
580 } else {
581 ret[0] = idx[1] - len[1];
582 }
583
584 if (minus1 && numFound) {
585 ret[0] = -ret[0];
586 }
587
588 return ret[0] != 0;
589 } else if (idx[1] == len[1]) {
590 if (!Character.isDigit(s1.charAt(idx[0]))) {
591 ret[0] = nComp;
592 } else {
593 ret[0] = len[0] - idx[0];
594 }
595
596 if (minus1 && numFound) {
597 ret[0] = -ret[0];
598 }
599
600 return ret[0] != 0;
601 }
602
603 c1 = s1.charAt(idx[0]++);
604 c2 = s2.charAt(idx[1]++);
605 }
606
607 if (Character.isDigit(c2)) {
608 if (numFound) {
609 ret[0] = -1;
610 } else {
611 ret[0] = c1 - c2;
612 }
613
614 if (minus2 && numFound) {
615 ret[0] = -ret[0];
616 }
617
618 return ret[0] != 0;
619
620
621
622 }
623
624 if (nComp != 0) {
625 ret[0] = minus1 ? -nComp : nComp;
626
627 return true;
628 }
629
630
631 if (isDecSeparator(c1)) {
632 if (isDecSeparator(c2)) {
633 if (sComp && c1 != c2) {
634 ret[1] = c1 - c2;
635 sComp = false;
636 }
637
638 if (idx[0] == len[0] || idx[1] == len[1]) {
639 return false;
640 }
641
642 c1 = s1.charAt(idx[0]++);
643 c2 = s2.charAt(idx[1]++);
644 decFound = true;
645 } else {
646
647 if (sComp) {
648 ret[1] = c1 - c2;
649 sComp = false;
650 }
651
652 if (idx[0] == len[0]) {
653 return false;
654 }
655
656 c1 = s1.charAt(idx[0]++);
657
658 while (c1 == '0') {
659 if (idx[0] == len[0]) {
660 return false;
661 }
662
663 c1 = s1.charAt(idx[0]++);
664 }
665 }
666 } else if (isDecSeparator(c2)) {
667
668 if (sComp) {
669 ret[1] = c1 - c2;
670 sComp = false;
671 }
672
673 if (idx[1] == len[1]) {
674 return false;
675 }
676
677 c2 = s2.charAt(idx[1]++);
678
679 while (c2 == '0') {
680 if (idx[1] == len[1]) {
681 return false;
682 }
683
684 c2 = s2.charAt(idx[1]++);
685 }
686 if (softEquals) return false;
687 ret[0] = minus1 ? 1 : -1;
688
689 return true;
690 }
691
692
693 if (decFound) {
694
695 while (true) {
696 if (Character.isDigit(c1)) {
697 if (Character.isDigit(c2)) {
698 numFound = true;
699
700 if (c1 != c2) {
701 ret[0] = c1 - c2;
702
703 if (minus1) {
704 ret[0] = -ret[0];
705 }
706
707 return true;
708 }
709 } else {
710 if (sComp) {
711 ret[1] = c1 - c2;
712 sComp = false;
713 }
714
715 while (c1 == '0') {
716
717 if (idx[0] == len[0]) {
718 return false;
719 }
720
721 c1 = s1.charAt(idx[0]++);
722 }
723
724 if (Character.isDigit(c1)) {
725 ret[0] = 1;
726 }
727
728 if (minus1) {
729 ret[0] = -ret[0];
730 }
731
732 return ret[0] != 0;
733 }
734 } else if (Character.isDigit(c2)) {
735 if (sComp) {
736 ret[1] = c1 - c2;
737 sComp = false;
738 }
739
740 while (c2 == '0') {
741
742 if (idx[1] == len[1]) {
743 return false;
744 }
745
746 c2 = s2.charAt(idx[1]++);
747 }
748
749 if (Character.isDigit(c2)) {
750 ret[0] = -1;
751 }
752
753 if (minus1) {
754 ret[0] = -ret[0];
755 }
756
757 return ret[0] != 0;
758 } else if (!numFound || nComp == 0) {
759 return false;
760 }
761
762 if (idx[0] == len[0] || idx[1] == len[1]) {
763 return false;
764 }
765
766 c1 = s1.charAt(idx[0]++);
767 c2 = s2.charAt(idx[1]++);
768 }
769 }
770
771
772 if (numFound) {
773
774 while (isSeparator(c1)) {
775 if (sComp) {
776 if (c1 != c2) {
777 ret[1] = c1 - c2;
778 sComp = false;
779 } else {
780 if (idx[1] == len[1]) {
781 return false;
782 }
783
784 c2 = s2.charAt(idx[1]++);
785 }
786 }
787
788 if (idx[0] == len[0]) {
789 return false;
790 }
791
792 c1 = s1.charAt(idx[0]++);
793 }
794
795 ;
796
797
798 while (isSeparator(c2)) {
799 if (sComp) {
800 if (c1 != c2) {
801 ret[1] = c1 - c2;
802 sComp = false;
803 } else {
804 if (idx[0] == len[0]) {
805 return false;
806 }
807
808 c1 = s1.charAt(idx[0]++);
809 }
810 }
811
812 c2 = s2.charAt(idx[1]++);
813 }
814 } else {
815 if (ret[1] != 0) {
816 ret[0] = ret[1];
817
818 if (minus1) {
819 ret[0] = -ret[0];
820 }
821
822 return true;
823 }
824 }
825
826 if (!numFound) {
827 if (c1 != c2) {
828 ret[0] = c1 - c2;
829
830 if (minus1) {
831 ret[0] = -ret[0];
832 }
833
834 return true;
835 }
836 } else {
837 idx[0]--;
838 idx[1]--;
839 }
840
841 return false;
842 }
843
844
845
846
847
848
849
850
851 public boolean isSoftEquals()
852 {
853 return softEquals;
854 }
855
856
857
858
859
860
861
862
863 public void setSoftEquals(boolean softEquals)
864 {
865 this.softEquals = softEquals;
866 }
867 }
868
869