< 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 +1,7 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * 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,6 +93,205 @@
@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 >