/* * Copyright (c) 2002-2015, JIDE Software Inc. 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 * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */ package jidefx.utils.comparator; import java.io.Serializable; import java.util.Comparator; /** * A Comparator that compares CharSequence objects (including String and StringBuffer as both extend CharSequence. * Throws ClassCastExceptions if the objects are not CharSequence, or if they are null. If both objects are null, they * will be treated as equal. If one is null and the other is not, the null value will be treated as smaller then * non-null value. */ public class CharSequenceComparator implements Comparator<Object>, Serializable { private static final long serialVersionUID = -4749963150099170895L; private boolean _caseSensitive; public static final ComparatorContext CONTEXT = new ComparatorContext("IgnoreLocale"); //NON-NLS public static final ComparatorContext CONTEXT_IGNORE_CASE = new ComparatorContext("IgnoreLocale_Ignorecase"); //NON-NLS /** * Constructs a CharSequenceComparator. */ public CharSequenceComparator() { this(true); } public CharSequenceComparator(boolean caseSensitive) { _caseSensitive = caseSensitive; } /** * Checks if the case is sensitive when comparing. * * @return true if the comparator is case sensitive. */ public boolean isCaseSensitive() { return _caseSensitive; } /** * Sets the case sensitive flag. By default, it's true meaning the comparator is case sensitive. * * @param caseSensitive true or false. */ public void setCaseSensitive(boolean caseSensitive) { _caseSensitive = caseSensitive; } public int compare(Object o1, Object o2) { if (o1 == null && o2 == null) { return 0; } else if (o1 == null) { return -1; } else if (o2 == null) { return 1; } if (o1 instanceof CharSequence) { if (o2 instanceof CharSequence) { CharSequence s1 = (CharSequence) o1; CharSequence s2 = (CharSequence) o2; return isCaseSensitive() ? compareCase(s1, s2) : compareIgnoreCase(s1, s2); } else { // o2 wasn't comparable throw new ClassCastException("The second argument of this method was not a CharSequence: " + o2.getClass().getName()); } } else if (o2 instanceof Comparable) { // o1 wasn't comparable throw new ClassCastException("The first argument of this method was not a CharSequence: " + o1.getClass().getName()); } else { // neither were comparable throw new ClassCastException("Both arguments of this method were not CharSequences: " + o1.getClass().getName() + " and " + o2.getClass().getName()); } } private int compareCase(CharSequence s1, CharSequence s2) { int len1 = s1.length(); int len2 = s2.length(); int n = Math.min(len1, len2); int k = 0; while (k < n) { char c1 = s1.charAt(k); char c2 = s2.charAt(k); if (c1 != c2) { return c1 - c2; } k++; } return len1 - len2; } private int compareIgnoreCase(CharSequence s1, CharSequence s2) { int n1 = s1.length(), n2 = s2.length(); for (int i1 = 0, i2 = 0; i1 < n1 && i2 < n2; i1++, i2++) { char c1 = s1.charAt(i1); char c2 = s2.charAt(i2); if (c1 != c2) { c1 = Character.toUpperCase(c1); c2 = Character.toUpperCase(c2); if (c1 != c2) { c1 = Character.toLowerCase(c1); c2 = Character.toLowerCase(c2); if (c1 != c2) { return c1 - c2; } } } } return n1 - n2; } }