001package jmri.util;
002
003import java.util.Comparator;
004import java.util.regex.Pattern;
005
006/**
007 * A comparator for mixed Strings containing a mix of words and numbers:  "ATSF 123",
008 * "CN 100B", etc.
009 *
010 * Regex implementation first, but that might be too slow
011 */
012
013public class ChunkyNumbersComparator implements Comparator<String> {
014
015    @Override
016    public int compare(String s1, String s2) {
017        
018        var m1 = sortPattern.matcher(s1);
019        var m2 = sortPattern.matcher(s2);
020       
021        if (!m1.find() || !m2.find()) {
022            log.warn("Failed to parse '{}' '{}'", s1, s2);
023            return s1.compareTo(s2);
024        }
025        
026        // note increment by 2, because of two cases inside the loop
027        for (int index = 1; index < 6; index = index + 2) {
028        
029            // check first alpha group
030            String g1 = m1.group(index);
031            String g2 = m2.group(index);
032            int len1 = g1.length();
033            int len2 = g2.length();
034            
035            // Is there at least one letter group?
036            if (len1 > 0 || len2 > 0) {
037                int result = g1.compareTo(g2);
038                if (result !=0) {
039                    return result;
040                }
041            }
042    
043            // check number group
044            g1 = m1.group(index+1);
045            g2 = m2.group(index+1);
046            
047            boolean g1e = g1.isEmpty();
048            boolean g2e = g2.isEmpty();
049            if (g1e && g2e) {
050                // no match, so done (can't be another letter group, would have been picked up above)
051                return 0;
052            }
053            if (g1e) {
054                return -1;
055            }
056            if (g2e) {
057                return +1;
058            }
059            
060            // Here an number group in each input, parse as numbers and compare
061            Long v1;
062            Long v2;
063            try {
064                v1 = Long.valueOf(g1);
065                v2 = Long.valueOf(g2);
066            } catch (NumberFormatException e1) {
067                log.warn("Comparison should not have reached here with {} {}", g1, g2);
068                return s1.compareTo(s2);
069            }
070            // check for parsed numbers
071            int result = v1.compareTo(v2);
072            if (result !=0) {
073                return result;
074            }  
075
076            // reached here, go around again
077        }
078        // reached here, didn't find a difference
079        return 0;
080    }
081
082    static final Pattern sortPattern = Pattern.compile("([^0-9]*)([0-9]*)([^0-9]*)([0-9]*)([^0-9]*)([0-9]*)");
083    
084    //private final boolean isDigit(char ch) {
085    //    return (('0' <= ch) && (ch <= '9'));
086    //}
087
088    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ChunkyNumbersComparator.class);
089
090}