< prev index next >

src/java.base/share/classes/java/util/Comparators.java

Print this page
rev 17656 : [mq]: 8134512-provide-Alpha-Numeric-logical-Comparator

*** 1,7 **** /* ! * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this --- 1,7 ---- /* ! * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this
*** 93,98 **** --- 93,297 ---- @Override public Comparator<T> reversed() { return new NullComparator<>(!nullFirst, real == null ? null : real.reversed()); } } + + /** + * The comparator for comparing character sequences that consist solely + * of decimal digits. The result of comparing is as if the values were + * compared numerically. + */ + private static class DecimalComparator implements Comparator<CharSequence>, + Serializable { + + private static final long serialVersionUID = 0x661e8cc550c7704dL; + + private static final Comparator<CharSequence> + DECIMAL_COMPARATOR_LEADING_ZEROES_FIRST = + new DecimalComparator(true) { + @Override + public Comparator<CharSequence> reversed() { + return DECIMAL_COMPARATOR_LEADING_ZEROES_FIRST_REVERSED; + } + }; + + private static final Comparator<CharSequence> + DECIMAL_COMPARATOR_LEADING_ZEROES_LAST = + new DecimalComparator(false) { + @Override + public Comparator<CharSequence> reversed() { + return DECIMAL_COMPARATOR_LEADING_ZEROES_LAST_REVERSED; + } + }; + + private static final Comparator<CharSequence> + DECIMAL_COMPARATOR_LEADING_ZEROES_FIRST_REVERSED = + new DecimalComparator(true) { + @Override + public Comparator<CharSequence> reversed() { + return DECIMAL_COMPARATOR_LEADING_ZEROES_FIRST; + } + @Override + public int compare(CharSequence cs1, CharSequence cs2) { + return super.compare(cs2, cs1); + } + }; + + private static final Comparator<CharSequence> + DECIMAL_COMPARATOR_LEADING_ZEROES_LAST_REVERSED = + new DecimalComparator(false) { + @Override + public Comparator<CharSequence> reversed() { + return DECIMAL_COMPARATOR_LEADING_ZEROES_LAST; + } + @Override + public int compare(CharSequence cs1, CharSequence cs2) { + return super.compare(cs2, cs1); + } + }; + + private final boolean leadingZeroesFirst; + + private DecimalComparator(boolean leadingZeroesFirst) { + this.leadingZeroesFirst = leadingZeroesFirst; + } + + static Comparator<CharSequence> getInstance(boolean leadingZeroesFirst) { + return leadingZeroesFirst ? DECIMAL_COMPARATOR_LEADING_ZEROES_FIRST + : DECIMAL_COMPARATOR_LEADING_ZEROES_LAST; + } + + private boolean canSkipLeadingZeroes(CharSequence s, int len) { + for (int i = 0; i < len;) { + int cp = Character.codePointAt(s, i); + if (Character.digit(cp, 10) != 0) + return false; + i += Character.charCount(cp); + } + return true; + } + + @Override + public int compare(CharSequence cs1, CharSequence cs2) { + int len1 = Character.codePointCount(cs1, 0, cs1.length()); + int len2 = Character.codePointCount(cs2, 0, cs2.length()); + int dlen = len1 - len2; + if (len1 == 0 || len2 == 0) { + return dlen; + } else if (dlen > 0) { + if (!canSkipLeadingZeroes(cs1, dlen)) + return 1; + int off = Character.offsetByCodePoints(cs1, 0, dlen); + cs1 = cs1.subSequence(off, cs1.length()); + } else if (dlen < 0) { + if (!canSkipLeadingZeroes(cs2, -dlen)) + return -1; + int off = Character.offsetByCodePoints(cs2, 0, -dlen); + cs2 = cs2.subSequence(off, cs2.length()); + } + int cmp = 0; + for (int i1 = 0, i2 = 0; i1 < cs1.length(); ) { + int cp1 = Character.codePointAt(cs1, i1); + int cp2 = Character.codePointAt(cs2, i2); + if (cp1 != cp2) { + if (cmp == 0) { + cmp = cp1 - cp2; + } + int cmpNum = Character.digit(cp1, 10) - + Character.digit(cp2, 10); + if (cmpNum != 0) { + return cmpNum; + } + } + i1 += Character.charCount(cp1); + i2 += Character.charCount(cp2); + } + return dlen == 0 ? cmp : (leadingZeroesFirst ^ (dlen < 0) ? -1 : 1); + } + } + + /** + * Compares char sequences, taking into account their numeric part if one + * exists. + * + * @see Comparator#comparingAlphaDecimal(Comparator) + * @see Comparator#comparingAlphaDecimalLeadingZeroesFirst(Comparator) + */ + static class AlphaDecimalComparator<T extends CharSequence> + implements Comparator<T>, Serializable { + + private static final long serialVersionUID = 0xd7a7129b9ca1056dL; + + private final Comparator<? super CharSequence> alphaComparator; + private final Comparator<CharSequence> decimalComparator; + + AlphaDecimalComparator(Comparator<? super CharSequence> alphaComparator, + boolean leadingZeroesFirst) { + this(alphaComparator, DecimalComparator.getInstance(leadingZeroesFirst)); + } + + private AlphaDecimalComparator(Comparator<? super CharSequence> alphaComparator, + Comparator<CharSequence> decimalComparator) { + this.alphaComparator = alphaComparator; + this.decimalComparator = decimalComparator; + } + + @Override + public Comparator<T> reversed() { + return new AlphaDecimalComparator<>(alphaComparator.reversed(), + decimalComparator.reversed()); + } + + @Override + public int compare(T cs1, T cs2) { + Decomposer d1 = new Decomposer(cs1); + Decomposer d2 = new Decomposer(cs2); + for (;;) { + int cmp; + if ((cmp = alphaComparator.compare(d1.get(), d2.get())) != 0 || + (cmp = decimalComparator.compare(d1.get(), d2.get())) != 0) + { + return cmp; + } + if (d1.eos() && d2.eos()) return 0; + } + } + + /** + * Given a CharSequence, splits it into a series of subsequences so that + * every character of the very first subsequence (possibly empty) is + * not a decimal digit; then every character of the second subsequence + * is a decimal digit, and so on. + */ + private static class Decomposer { + private final CharSequence sequence; + private boolean expectingDecimal = false; + private int index = 0; + + Decomposer(CharSequence sequence) { + this.sequence = sequence; + } + + CharSequence get() { + int start = index, end = start, len = sequence.length() - start; + while (len > 0) { + int cp = Character.codePointAt(sequence, end); + int ct = Character.getType(cp); + boolean isDecimal = (ct == Character.DECIMAL_DIGIT_NUMBER); + if (isDecimal ^ expectingDecimal) { + break; + } + int cpWidth = Character.charCount(cp); + end += cpWidth; + len -= cpWidth; + } + expectingDecimal = !expectingDecimal; + return sequence.subSequence(start, index = end); + } + + boolean eos() { + return index >= sequence.length(); + } + } + } }
< prev index next >