001package org.unix4j.util.sort;
002
003import java.util.Comparator;
004
005import org.unix4j.util.StringUtil;
006
007/** 
008 * Comparison based on {@link Double} floating point numbers. This 
009 * allows floating point numbers to be specified in scientific notation, 
010 * like 1.0e-34 and 10e100 using {@link Double#parseDouble(String)}. Leading
011 * and trailing strings are ignored. If the string is not a valid number, it is
012 * treated as {@link Double#NaN} during the comparison.
013 */
014public class ScientificNumberStringComparator implements Comparator<CharSequence> {
015        
016        /**
017         * The singleton instance.
018         */
019        public static final ScientificNumberStringComparator INSTANCE = new ScientificNumberStringComparator();
020        
021        /**
022         * Private constructor for the singleton {@link #INSTANCE}.
023         */
024        private ScientificNumberStringComparator () {
025                super();
026        }
027        
028        @Override
029        public int compare(CharSequence s1, CharSequence s2) {
030                final int start1 = StringUtil.findStartTrimWhitespace(s1);
031                final int end1 = StringUtil.findEndTrimWhitespace(s1);
032                final int start2 = StringUtil.findStartTrimWhitespace(s2);
033                final int end2 = StringUtil.findEndTrimWhitespace(s2);
034                final String str1 = s1.subSequence(start1, end1).toString();
035                final String str2 = s2.subSequence(start2, end2).toString();
036                final double dbl1 = parseDouble(str1);
037                final double dbl2 = parseDouble(str2);
038                final boolean isNan1 = Double.isNaN(dbl1); 
039                final boolean isNan2 = Double.isNaN(dbl2); 
040                if (isNan1 || isNan2) {
041                        final boolean isNonDouble1 = isNan1 && !"NaN".equals(str1); 
042                        final boolean isNonDouble2 = isNan2 && !"NaN".equals(str2); 
043                        if (isNonDouble1 && isNonDouble2) {
044                                return str1.compareTo(str2);
045                        }
046                        if (isNonDouble1) return -1;
047                        if (isNonDouble2) return 1;
048                }
049                return Double.compare(dbl1, dbl2);
050        }
051        private static double parseDouble(String s) {
052                try {
053                        return Double.parseDouble(s);
054                } catch (NumberFormatException e) {
055                        return Double.NaN;
056                }
057        }
058}