/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program 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.
*
* This program 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 at /legal/license.txt).
*
* 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
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.j2me.global;
import javax.microedition.global.StringComparator;
import javax.microedition.global.UnsupportedLocaleException;
/**
* This class realizes string comparison methods of
* {@link javax.microedition.global.StringComparator}. Specifically, these are:
* <ul>
* <li> {@link #compare(String, String)}
* <li> {@link #equals(String, String)}
* </ul>
*/
public class StringComparatorImpl implements CommonStringComparator {
/** The capacity increment value of the internal buffers. */
private static final int CAPACITY_INCREMENT = 64;
/** Current collation level. */
private int level;
/**
* Internal buffer used in the compare function for temporary data
* storage.
*/
private int[] buffer1 = new int[CAPACITY_INCREMENT];
/**
* Internal buffer used in the compare function for temporary data
* storage.
*/
private int[] buffer2 = new int[CAPACITY_INCREMENT];
/** Internal buffers length. */
int buffersLength = CAPACITY_INCREMENT;
/** Normalization table */
private final NormalizationTable nrmTable;
/** String normalizer 1 */
private final StringNormalizer strNormalizer1;
/** String normalizer 2 */
private final StringNormalizer strNormalizer2;
/** String collator 1 */
private final StringCollator strCollator1;
/** String collator 2 */
private final StringCollator strCollator2;
/**
* Constructs an instance using the specified locale and collation level.
* <p>
* If the specified locale is <code>null</code> or the empty string, this
* class uses the generic collation algorithm.
*
* @param locale the locale to use, or <code>null</code> to use the generic
* collation algorithm
* @param level the collation level to use
* @throws UnsupportedLocaleException if the specified locale
* is not supported by the <code>StringComparator</code> implementation
* @throws IllegalArgumentException if the specified locale is
* non-<code>null</code> but is not valid according to the MIDP 2.0
* specification, or if <code>level</code> is not one of the constants
* defined in this class
* @see javax.microedition.global.StringComparator#LEVEL1
* @see javax.microedition.global.StringComparator#LEVEL2
* @see javax.microedition.global.StringComparator#LEVEL3
* @see javax.microedition.global.StringComparator#IDENTICAL
*/
public StringComparatorImpl(String locale, int level) {
this.level = level;
CollationElementTable colTable = null;
nrmTable = NormalizationTableImpl.getInstance();
colTable = CollationElementTableImpl.getInstance(locale);
strNormalizer1 = new StringNormalizer(nrmTable);
strCollator1 = new StringCollator(strNormalizer1, colTable);
strNormalizer2 = new StringNormalizer(nrmTable);
strCollator2 = new StringCollator(strNormalizer2, colTable);
}
/**
* Test if the given character is unsafe to start the UCA with.
*
* @param c the tested character
* @return <code>true</code> if the character is unsafe to start the UCA
* with
*/
private final boolean isUnsafeCharacter(char c) {
if ((c & 0xf800) == 0xd800) {
// low or high surrogate
return true;
}
int value = nrmTable.getCanonicalDecomposition(buffer1, 0, c);
if (!nrmTable.isSingleCodePoint(value)) {
return true;
}
return nrmTable.isUnsafe(value);
}
/**
* Returns the starting offset for the UCA comparison. Tries to skip
* the first characters from both input strings which are equal. This speeds
* up the comparison.
*
* @param s1 the first string
* @param s2 the second string
* @return the starting offset
*/
private final int skipBeginning(String s1, String s2) {
int offset;
int length = s1.length();
if (s2.length() < length) {
length = s2.length();
}
if (length == 0) {
return 0;
}
if (s1.charAt(0) != s2.charAt(0)) {
return 0;
}
for (offset = 1; offset < length; ++offset) {
if (s1.charAt(offset) != s2.charAt(offset)) {
if (!isUnsafeCharacter(s1.charAt(offset)) &&
!isUnsafeCharacter(s2.charAt(offset))) {
return offset;
}
for (--offset; offset > 0; --offset) {
if (!isUnsafeCharacter(s1.charAt(offset))) {
return offset;
}
}
return offset;
}
}
if (length < s1.length()) {
if (!isUnsafeCharacter(s1.charAt(length))) {
return length;
}
for (--length; length > 0; --length) {
if (!isUnsafeCharacter(s1.charAt(length))) {
return length;
}
}
return length;
}
if (length < s2.length()) {
if (!isUnsafeCharacter(s2.charAt(length))) {
return length;
}
for (--length; length > 0; --length) {
if (!isUnsafeCharacter(s1.charAt(length))) {
return length;
}
}
return length;
}
return length;
}
/**
* Compares the two strings using the rules specific to the associated
* locale of this instance. Use the <code>equals</code> method to more
* conveniently test for equality.
*
* @param s1 first string to compare
* @param s2 second string to compare
* @return negative if <code>s1</code> belongs before <code>s2</code>,
* zero if the strings are equal, positive if <code>s1</code> belongs
* after <code>s2</code>
* @throws NullPointerException if either <code>s1</code> or <code>s2</code>
* is <code>null</code>
* @see #equals(String, String)
*/
public int compare(String s1, String s2) {
synchronized (this) {
// try to skip as much as possible
int offset = skipBeginning(s1, s2);
// initialize objects
strNormalizer1.setSource(s1, offset);
strNormalizer2.setSource(s2, offset);
strCollator1.reset();
strCollator2.reset();
int idx1;
int idx2;
int result;
int buffered = 0;
int value1;
int value2;
idx1 = 0;
idx2 = 0;
do {
if (buffered >= buffersLength) {
buffersLength += CAPACITY_INCREMENT;
int[] newBuffer1 =
new int[buffersLength];
int[] newBuffer2 =
new int[buffersLength];
System.arraycopy(buffer1, 0, newBuffer1, 0, buffered);
System.arraycopy(buffer2, 0, newBuffer2, 0, buffered);
buffer1 = newBuffer1;
buffer2 = newBuffer2;
}
buffer1[buffered] = strCollator1.getNextElement();
buffer2[buffered] = strCollator2.getNextElement();
++buffered;
value1 = buffer1[idx1];
value2 = buffer2[idx2];
if (value1 != -1) {
value1 = CollationElementTable.getL1(value1);
}
if (value2 != -1) {
value2 = CollationElementTable.getL1(value2);
}
if (value1 != 0) {
if (value2 != 0) {
result = value1 - value2;
if (result != 0) {
return result;
}
++idx1;
}
++idx2;
} else {
if (value2 == 0) {
++idx2;
}
++idx1;
}
} while ((value1 != -1) || (value2 != -1));
if (level < StringComparator.LEVEL2) {
return 0;
}
idx1 = 0;
idx2 = 0;
do {
value1 = buffer1[idx1];
value2 = buffer2[idx2];
if (value1 != -1) {
value1 = CollationElementTable.getL2(value1);
}
if (value2 != -1) {
value2 = CollationElementTable.getL2(value2);
}
if (value1 != 0) {
if (value2 != 0) {
result = value1 - value2;
if (result != 0) {
return result;
}
++idx1;
}
++idx2;
} else {
if (value2 == 0) {
++idx2;
}
++idx1;
}
} while ((value1 != -1) || (value2 != -1));
if (level < StringComparator.LEVEL3) {
return 0;
}
idx1 = 0;
idx2 = 0;
do {
value1 = buffer1[idx1];
value2 = buffer2[idx2];
if (value1 != -1) {
value1 = CollationElementTable.getL3(value1);
}
if (value2 != -1) {
value2 = CollationElementTable.getL3(value2);
}
if (value1 != 0) {
if (value2 != 0) {
result = value1 - value2;
if (result != 0) {
return result;
}
++idx1;
}
++idx2;
} else {
if (value2 == 0) {
++idx2;
}
++idx1;
}
} while ((value1 != -1) || (value2 != -1));
if (level == StringComparator.IDENTICAL) {
strNormalizer1.reset();
strNormalizer2.reset();
do {
value1 = strNormalizer1.getNextElement();
value2 = strNormalizer2.getNextElement();
if (value1 != -1) {
value1 = NormalizationTable.getCodePoint(value1);
}
if (value2 != -1) {
value2 = NormalizationTable.getCodePoint(value2);
}
result = value1 - value2;
if (result != 0) {
return result;
}
} while ((value1 != -1) || (value2 != -1));
}
}
return 0;
}
/**
* Tests if the two strings are equal according to the rules specific to
* the associated locale of this instance.
*
* @param s1 first string to compare
* @param s2 second string to compare
* @return <code>true</code> if the strings are equal, <code>false</code>
* if not
* @throws NullPointerException if either s1 or s2 is <code>null</code>
*/
public boolean equals(String s1, String s2) {
return compare(s1, s2) == 0;
}
}
|