package client.net.sf.saxon.ce.expr.sort;
import client.net.sf.saxon.ce.lib.StringCollator;
/**
* A StringCollator that sorts lowercase before uppercase, or vice versa.
*
* <p>Case is irrelevant, unless the strings are equal ignoring
* case, in which case lowercase comes first.</p>
*
* @author Michael H. Kay
*/
public class CaseFirstCollator implements StringCollator {
private StringCollator baseCollator;
private boolean upperFirst;
/**
* Create a CaseFirstCollator
* @param base the base collator, which determines how characters are sorted irrespective of case
* @param upperFirst true if uppercase precedes lowercase, false otherwise
*/
public CaseFirstCollator(StringCollator base, boolean upperFirst) {
this.baseCollator = base;
this.upperFirst = upperFirst;
}
/**
* Compare two string objects: case is irrelevant, unless the strings are equal ignoring
* case, in which case lowercase comes first.
*
* @return <0 if a<b, 0 if a=b, >0 if a>b
* @throws ClassCastException if the objects are of the wrong type for this Comparer
*/
public int compareStrings(String a, String b) {
int diff = baseCollator.compareStrings(a, b);
if (diff != 0) {
return diff;
}
// This is doing a character-by-character comparison, which isn't really right.
// There might be a sequence of letters constituting a single collation unit.
int i = 0;
int j = 0;
while (true) {
// Skip characters that are equal in the two strings
while (i < a.length() && j < b.length() && a.charAt(i) == b.charAt(j)) {
i++;
j++;
}
// Skip non-letters in the first string
while (i < a.length() && !Character.isLetter(a.charAt(i))) {
i++;
}
// Skip non-letters in the second string
while (j < b.length() && !Character.isLetter(b.charAt(j))) {
j++;
}
// If we've got to the end of either string, treat the strings as equal
if (i >= a.length()) {
return 0;
}
if (j >= b.length()) {
return 0;
}
// If one of the characters is upper/lower case and the other isn't, the issue is decided
boolean aFirst = (upperFirst ? Character.isUpperCase(a.charAt(i++)) : Character.isLowerCase(a.charAt(i++)));
boolean bFirst = (upperFirst ? Character.isUpperCase(b.charAt(j++)) : Character.isLowerCase(b.charAt(j++)));
if (aFirst && !bFirst) {
return -1;
}
if (bFirst && !aFirst) {
return +1;
}
}
}
/**
* Compare two strings for equality. This may be more efficient than using compareStrings and
* testing whether the result is zero, but it must give the same result
* @param s1 the first string
* @param s2 the second string
* @return true if and only if the strings are considered equal,
*/
public boolean comparesEqual(String s1, String s2) {
return compareStrings(s1, s2) == 0;
}
/**
* Get a collation key for two Strings. The essential property of collation keys
* is that if two values are equal under the collation, then the collation keys are
* compare correctly under the equals() method.
*/
public Object getCollationKey(String s) {
return baseCollator.getCollationKey(s);
}
}
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.