changeset: 165299:9d1dca2f0361 tag: tip user: Vladimir Voskresensky date: Tue Mar 30 14:56:28 2010 +0400 summary: patch for #183162 - provide efficient char sequence implementation diff --git a/openide.util/manifest.mf b/openide.util/manifest.mf --- a/openide.util/manifest.mf +++ b/openide.util/manifest.mf @@ -1,5 +1,5 @@ Manifest-Version: 1.0 OpenIDE-Module: org.openide.util OpenIDE-Module-Localizing-Bundle: org/openide/util/Bundle.properties -OpenIDE-Module-Specification-Version: 8.2 +OpenIDE-Module-Specification-Version: 8.3 diff --git a/openide.util/src/org/openide/util/CharSequences.java b/openide.util/src/org/openide/util/CharSequences.java new file mode 100644 --- /dev/null +++ b/openide.util/src/org/openide/util/CharSequences.java @@ -0,0 +1,908 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ +package org.openide.util; + +import java.util.Arrays; +import java.util.Comparator; + +/** + * utilities to provide and work with compact CharSequence implementations + * + * @since 8.3 + * @author Alexander Simon + * @author Vladiir Voskresensky + */ +public class CharSequences { + + + /** + * provides compact char sequence object like {@link String#String(char[], int, int)} + */ + public static CharSequence create(char buf[], int start, int count) { + if (start < 0) { + throw new StringIndexOutOfBoundsException(start); + } + if (count < 0) { + throw new StringIndexOutOfBoundsException(count); + } + // Note: offset or count might be near -1>>>1. + if (start > buf.length - count) { + throw new StringIndexOutOfBoundsException(start + count); + } + int n = count; + if (n == 0) { + // special constant shared among all empty char sequences + return EMPTY; + } + byte[] b = new byte[n]; + boolean bytes = true; + int o; + // check 2 bytes vs 1 byte chars + for (int i = 0; i < n; i++) { + o = buf[start + i]; + if ((o & 0xFF) != o) { + // can not compact this char sequence + bytes = false; + break; + } + b[i] = (byte) o; + } + if (bytes) { + return createFromBytes(b, n); + } + char[] v = new char[count]; + System.arraycopy(buf, start, v, 0, count); + return new CharSequenceKey(v); + } + + /** + * provides compact char sequence object like {@link String#String(String)} + */ + public static CharSequence create(CharSequence s) { + if (s == null) { + return null; + } + // already compact instance + if (s instanceof CompactCharSequence) { + return s; + } + int n = s.length(); + if (n == 0) { + // special constant shared among all empty char sequences + return EMPTY; + } + byte[] b = new byte[n]; + boolean bytes = true; + int o; + for (int i = 0; i < n; i++) { + o = s.charAt(i); + if ((o & 0xFF) != o) { + bytes = false; + break; + } + b[i] = (byte) o; + } + if (bytes) { + return createFromBytes(b, n); + } + char[] v = new char[n]; + for (int i = 0; i < n; i++) { + v[i] = s.charAt(i); + } + return new CharSequenceKey(v); + } + + /** + * provides optimized char sequences comparator + * @param ignoreCase true to get case insensitive comparator + * false to get case sensitive comparator + * @return comparator + */ + public static Comparator comparator(boolean ignoreCase) { + return ignoreCase ? ComparatorIgnoreCase : Comparator; + } + + /** + * returns object to represent empty sequence "" + * @return char sequence to represent empty sequence + */ + public static CharSequence empty() { + return EMPTY; + } + + /** + * predicate to check if provides char sequence is based on compact implementation + * @param cs char sequence object to check + * @return true if compact implementation, false otherwise + */ + public static boolean isCompact(CharSequence cs) { + return cs instanceof CompactCharSequence; + } + + /** + * Implementation of {@link String#indexOf(String)} for character sequences. + */ + public static int indexOf(CharSequence text, CharSequence seq) { + return indexOf(text, seq, 0); + } + + /** + * Implementation of {@link String#indexOf(String,int)} for character sequences. + */ + public static int indexOf(CharSequence text, CharSequence seq, int fromIndex) { + int textLength = text.length(); + int seqLength = seq.length(); + if (fromIndex >= textLength) { + return (seqLength == 0 ? textLength : -1); + } + if (fromIndex < 0) { + fromIndex = 0; + } + if (seqLength == 0) { + return fromIndex; + } + + char first = seq.charAt(0); + int max = textLength - seqLength; + + for (int i = fromIndex; i <= max; i++) { + // look for first character + if (text.charAt(i) != first) { + while (++i <= max && text.charAt(i) != first) { + } + } + + // found first character, now look at the rest of seq + if (i <= max) { + int j = i + 1; + int end = j + seqLength - 1; + for (int k = 1; j < end && text.charAt(j) == seq.charAt(k); j++, k++) { + } + if (j == end) { + // found whole sequence + return i; + } + } + } + return -1; + } + + private static CompactCharSequence createFromBytes(byte[] b, int n) { + if (n < 8) { + return new Fixed_1_7(b, n); + } else if (n < 16) { + return new Fixed_8_15(b, n); + } else if (n < 24) { + return new Fixed_16_23(b, n); + } + return new CharSequenceKey(b); + } + + /** + * compact char sequence implementation for strings in range 1-7 characters + * 8 + 2*4 = 16 bytes for all strings vs String impl occupying + */ + private static final class Fixed_1_7 implements CompactCharSequence, Comparable { + + private final int i1; + private final int i2; + + @SuppressWarnings("fallthrough") + private Fixed_1_7(byte[] b, int n) { + int a1 = n; + int a2 = 0; + switch (n) { + case 7: + a2 += (b[6] & 0xFF) << 24; + case 6: + a2 += (b[5] & 0xFF) << 16; + case 5: + a2 += (b[4] & 0xFF) << 8; + case 4: + a2 += b[3] & 0xFF; + case 3: + a1 += (b[2] & 0xFF) << 24; + case 2: + a1 += (b[1] & 0xFF) << 16; + case 1: + a1 += (b[0] & 0xFF) << 8; + case 0: + break; + default: + throw new IllegalArgumentException(); + } + i1 = a1; + i2 = a2; + } + + @Override + public int length() { + return i1 & 0xFF; + } + + @Override + public char charAt(int index) { + int r = 0; + switch (index) { + case 0: + r = (i1 & 0xFF00) >> 8; + break; + case 1: + r = (i1 & 0xFF0000) >> 16; + break; + case 2: + r = (i1 >> 24) & 0xFF; + break; + case 3: + r = i2 & 0xFF; + break; + case 4: + r = (i2 & 0xFF00) >> 8; + break; + case 5: + r = (i2 & 0xFF0000) >> 16; + break; + case 6: + r = (i2 >> 24) & 0xFF; + break; + } + return (char) r; + } + + @Override + public String toString() { + int n = length(); + char[] r = new char[n]; + for (int i = 0; i < n; i++) { + r[i] = charAt(i); + } + return new String(r); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof Fixed_1_7) { + Fixed_1_7 otherString = (Fixed_1_7) object; + return i1 == otherString.i1 && i2 == otherString.i2; + } + return false; + } + + @Override + public int hashCode() { + int hash = 0; + for (int i = 0; i < length(); i++) { + hash = 31 * hash + charAt(i); + } + return hash; + // return (i1 >> 4) + (i1 >> 8) + (i2 << 5) - i2; + } + + @Override + public CharSequence subSequence(int start, int end) { + return CharSequences.create(toString().substring(start, end)); + } + + @Override + public int compareTo(CharSequence o) { + return Comparator.compare(this, o); + } + } + + /** + * compact char sequence implementation for strings in range 8-15 characters + * size: 8 + 4*4 = 24 bytes for all strings vs String impl occupying + */ + private static final class Fixed_8_15 implements CompactCharSequence, Comparable { + + private final int i1; + private final int i2; + private final int i3; + private final int i4; + + @SuppressWarnings("fallthrough") + private Fixed_8_15(byte[] b, int n) { + int a1 = n; + int a2 = 0; + int a3 = 0; + int a4 = 0; + switch (n) { + case 15: + a4 += (b[14] & 0xFF) << 24; + case 14: + a4 += (b[13] & 0xFF) << 16; + case 13: + a4 += (b[12] & 0xFF) << 8; + case 12: + a4 += b[11] & 0xFF; + case 11: + a3 += (b[10] & 0xFF) << 24; + case 10: + a3 += (b[9] & 0xFF) << 16; + case 9: + a3 += (b[8] & 0xFF) << 8; + case 8: + a3 += b[7] & 0xFF; + case 7: + a2 += (b[6] & 0xFF) << 24; + case 6: + a2 += (b[5] & 0xFF) << 16; + case 5: + a2 += (b[4] & 0xFF) << 8; + case 4: + a2 += b[3] & 0xFF; + case 3: + a1 += (b[2] & 0xFF) << 24; + case 2: + a1 += (b[1] & 0xFF) << 16; + case 1: + a1 += (b[0] & 0xFF) << 8; + case 0: + break; + default: + throw new IllegalArgumentException(); + } + i1 = a1; + i2 = a2; + i3 = a3; + i4 = a4; + } + + @Override + public int length() { + return i1 & 0xFF; + } + + @Override + public char charAt(int index) { + int r = 0; + switch (index) { + case 0: + r = (i1 & 0xFF00) >> 8; + break; + case 1: + r = (i1 & 0xFF0000) >> 16; + break; + case 2: + r = (i1 >> 24) & 0xFF; + break; + case 3: + r = i2 & 0xFF; + break; + case 4: + r = (i2 & 0xFF00) >> 8; + break; + case 5: + r = (i2 & 0xFF0000) >> 16; + break; + case 6: + r = (i2 >> 24) & 0xFF; + break; + case 7: + r = i3 & 0xFF; + break; + case 8: + r = (i3 & 0xFF00) >> 8; + break; + case 9: + r = (i3 & 0xFF0000) >> 16; + break; + case 10: + r = (i3 >> 24) & 0xFF; + break; + case 11: + r = i4 & 0xFF; + break; + case 12: + r = (i4 & 0xFF00) >> 8; + break; + case 13: + r = (i4 & 0xFF0000) >> 16; + break; + case 14: + r = (i4 >> 24) & 0xFF; + break; + } + return (char) r; + } + + @Override + public String toString() { + int n = length(); + char[] r = new char[n]; + for (int i = 0; i < n; i++) { + r[i] = charAt(i); + } + return new String(r); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof Fixed_8_15) { + Fixed_8_15 otherString = (Fixed_8_15) object; + return i1 == otherString.i1 && i2 == otherString.i2 && i3 == otherString.i3 && i4 == otherString.i4; + } + return false; + } + + @Override + public int hashCode() { + return i1 + 31 * (i2 + 31 * (i3 + 31 * i4)); + } + + @Override + public CharSequence subSequence(int start, int end) { + return CharSequences.create(toString().substring(start, end)); + } + + @Override + public int compareTo(CharSequence o) { + return Comparator.compare(this, o); + } + } + + /** + * compact char sequence implementation for strings in range 16-23 characters + * size: 8 + 3*8 = 32 bytes for all strings vs String impl occupying + */ + private static final class Fixed_16_23 implements CompactCharSequence, Comparable { + + private final long i1; + private final long i2; + private final long i3; + + @SuppressWarnings("fallthrough") + private Fixed_16_23(byte[] b, int n) { + long a1 = 0; + long a2 = 0; + long a3 = 0; + switch (n) { + case 23: + a3 += (b[22] & 0xFF) << 24; + case 22: + a3 += (b[21] & 0xFF) << 16; + case 21: + a3 += (b[20] & 0xFF) << 8; + case 20: + a3 += (b[19] & 0xFF); + a3 <<= 32; + case 19: + a3 += (b[18] & 0xFF) << 24; + case 18: + a3 += (b[17] & 0xFF) << 16; + case 17: + a3 += (b[16] & 0xFF) << 8; + case 16: + a3 += b[15] & 0xFF; + case 15: + a2 += (b[14] & 0xFF) << 24; + case 14: + a2 += (b[13] & 0xFF) << 16; + case 13: + a2 += (b[12] & 0xFF) << 8; + case 12: + a2 += (b[11] & 0xFF); + a2 <<= 32; + case 11: + a2 += (b[10] & 0xFF) << 24; + case 10: + a2 += (b[9] & 0xFF) << 16; + case 9: + a2 += (b[8] & 0xFF) << 8; + case 8: + a2 += b[7] & 0xFF; + case 7: + a1 += (b[6] & 0xFF) << 24; + case 6: + a1 += (b[5] & 0xFF) << 16; + case 5: + a1 += (b[4] & 0xFF) << 8; + case 4: + a1 += (b[3] & 0xFF); + a1 <<= 32; + case 3: + a1 += (b[2] & 0xFF) << 24; + case 2: + a1 += (b[1] & 0xFF) << 16; + case 1: + a1 += (b[0] & 0xFF) << 8; + case 0: + a1 += n; + break; + default: + throw new IllegalArgumentException(); + } + i1 = a1; + i2 = a2; + i3 = a3; + } + + @Override + public int length() { + return (int) (i1 & 0xFF); + } + + @Override + public char charAt(int index) { + int r = 0; + switch (index) { + case 0: + r = (int) ((i1 >> 8) & 0xFFL); + break; + case 1: + r = (int) ((i1 >> 16) & 0xFFL); + break; + case 2: + r = (int) ((i1 >> 24) & 0xFFL); + break; + case 3: + r = (int) ((i1 >> 32) & 0xFFL); + break; + case 4: + r = (int) ((i1 >> 40) & 0xFFL); + break; + case 5: + r = (int) ((i1 >> 48) & 0xFFL); + break; + case 6: + r = (int) ((i1 >> 56) & 0xFFL); + break; + case 7: + r = (int) (i2 & 0xFFL); + break; + case 8: + r = (int) ((i2 >> 8) & 0xFFL); + break; + case 9: + r = (int) ((i2 >> 16) & 0xFFL); + break; + case 10: + r = (int) ((i2 >> 24) & 0xFFL); + break; + case 11: + r = (int) ((i2 >> 32) & 0xFFL); + break; + case 12: + r = (int) ((i2 >> 40) & 0xFFL); + break; + case 13: + r = (int) ((i2 >> 48) & 0xFFL); + break; + case 14: + r = (int) ((i2 >> 56) & 0xFFL); + break; + case 15: + r = (int) (i3 & 0xFFL); + break; + case 16: + r = (int) ((i3 >> 8) & 0xFFL); + break; + case 17: + r = (int) ((i3 >> 16) & 0xFFL); + break; + case 18: + r = (int) ((i3 >> 24) & 0xFFL); + break; + case 19: + r = (int) ((i3 >> 32) & 0xFFL); + break; + case 20: + r = (int) ((i3 >> 40) & 0xFFL); + break; + case 21: + r = (int) ((i3 >> 48) & 0xFFL); + break; + case 22: + r = (int) ((i3 >> 56) & 0xFFL); + break; + } + return (char) r; + } + + @Override + public String toString() { + int n = length(); + char[] r = new char[n]; + for (int i = 0; i < n; i++) { + r[i] = charAt(i); + } + return new String(r); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof Fixed_16_23) { + Fixed_16_23 otherString = (Fixed_16_23) object; + return i1 == otherString.i1 && i2 == otherString.i2 && i3 == otherString.i3; + } + return false; + } + + @Override + public int hashCode() { + long res = i1 + 31 * (i2 + 31 * i3); + res = (res + (res >> 32)) & 0xFFFFFFFFL; + return (int) res; + } + + @Override + public CharSequence subSequence(int start, int end) { + return CharSequences.create(toString().substring(start, end)); + } + + @Override + public int compareTo(CharSequence o) { + return Comparator.compare(this, o); + } + } + + /** + * compact char sequence implementation based on byte[] or char[] array + * size: 8 + 4 + 4 (= 16 bytes) + sizeof ('value') + */ + private final static class CharSequenceKey implements CompactCharSequence, Comparable { + + private final Object value; + private int hash; + + private CharSequenceKey(byte[] b) { + value = b; + } + + private CharSequenceKey(char[] v) { + value = v; + } + + @Override + public int length() { + if (value instanceof byte[]) { + return ((byte[]) value).length; + } + return ((char[]) value).length; + } + + @Override + public char charAt(int index) { + if (value instanceof byte[]) { + int r = ((byte[]) value)[index] & 0xFF; + return (char) r; + } + return ((char[]) value)[index]; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof CharSequenceKey) { + CharSequenceKey otherString = (CharSequenceKey) object; + if (hash != 0 && otherString.hash != 0) { + if (hash != otherString.hash) { + return false; + } + } + if ((value instanceof byte[]) && (otherString.value instanceof byte[])) { + return Arrays.equals((byte[]) value, (byte[]) otherString.value); + } else if ((value instanceof char[]) && (otherString.value instanceof char[])) { + return Arrays.equals((char[]) value, (char[]) otherString.value); + } + } + return false; + } + + @Override + public int hashCode() { + int h = hash; + if (h == 0) { + if (value instanceof byte[]) { + byte[] v = (byte[]) value; + int n = v.length; + for (int i = 0; i < n; i++) { + h = 31 * h + v[i]; + } + } else { + char[] v = (char[]) value; + int n = v.length; + for (int i = 0; i < n; i++) { + h = 31 * h + v[i]; + } + } + hash = h; + } + return h; + } + + @Override + public CharSequence subSequence(int beginIndex, int endIndex) { + return CharSequences.create(toString().substring(beginIndex, endIndex)); + } + + @Override + public String toString() { + if (value instanceof byte[]) { + byte[] v = (byte[]) value; + int n = v.length; + char[] r = new char[n]; + for (int i = 0; i < n; i++) { + int c = v[i] & 0xFF; + r[i] = (char) c; + } + return new String(r); + } + char[] v = (char[]) value; + return new String(v); + } + + @Override + public int compareTo(CharSequence o) { + return Comparator.compare(this, o); + } + } + + private static final CompactCharSequence EMPTY = new CharSequenceKey(new byte[]{}); + private static final Comparator Comparator = new CharSequenceComparator(); + private static final Comparator ComparatorIgnoreCase = new CharSequenceComparatorIgnoreCase(); + + private static class CharSequenceComparator implements Comparator { + + @Override + public int compare(CharSequence o1, CharSequence o2) { + if ((o1 instanceof CharSequenceKey)) { + if ((o2 instanceof CharSequenceKey)) { + CharSequenceKey csk1 = (CharSequenceKey) o1; + CharSequenceKey csk2 = (CharSequenceKey) o2; + if ((csk1.value instanceof byte[]) + && (csk2.value instanceof byte[])) { + byte[] b1 = (byte[]) csk1.value; + byte[] b2 = (byte[]) csk2.value; + int len1 = b1.length; + int len2 = b2.length; + int n = Math.min(len1, len2); + int k = 0; + while (k < n) { + if (b1[k] != b2[k]) { + return (b1[k] & 0xFF) - (b2[k] & 0xFF); + } + k++; + } + return len1 - len2; + } + } else { + CharSequenceKey csk1 = (CharSequenceKey) o1; + if ((csk1.value instanceof byte[])) { + byte[] b1 = (byte[]) csk1.value; + int len1 = b1.length; + int len2 = o2.length(); + int n = Math.min(len1, len2); + int k = 0; + int c1, c2; + while (k < n) { + c1 = b1[k] & 0xFF; + c2 = o2.charAt(k); + if (c1 != c2) { + return c1 - c2; + } + k++; + } + return len1 - len2; + } + } + } else if ((o2 instanceof CharSequenceKey)) { + CharSequenceKey csk2 = (CharSequenceKey) o2; + if ((csk2.value instanceof byte[])) { + byte[] b2 = (byte[]) csk2.value; + int len1 = o1.length(); + int len2 = b2.length; + int n = Math.min(len1, len2); + int k = 0; + int c1, c2; + while (k < n) { + c1 = o1.charAt(k); + c2 = b2[k] & 0xFF; + if (c1 != c2) { + return c1 - c2; + } + k++; + } + return len1 - len2; + } + } + int len1 = o1.length(); + int len2 = o2.length(); + int n = Math.min(len1, len2); + int k = 0; + while (k < n) { + char c1 = o1.charAt(k); + char c2 = o2.charAt(k); + if (c1 != c2) { + return c1 - c2; + } + k++; + } + return len1 - len2; + } + } + + private static class CharSequenceComparatorIgnoreCase implements Comparator { + + @Override + public int compare(CharSequence o1, CharSequence o2) { + int n1 = o1.length(); + int n2 = o2.length(); + for (int i1 = 0, i2 = 0; i1 < n1 && i2 < n2; i1++, i2++) { + char c1 = o1.charAt(i1); + char c2 = o2.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; + } + } + + /** + * marker interface for compact char sequence implementations + */ + private interface CompactCharSequence extends CharSequence { + } + + /** + * private constructor for utilities class + */ + private CharSequences() { + } +} diff --git a/openide.util/test/unit/src/org/openide/util/CharSequencesTest.java b/openide.util/test/unit/src/org/openide/util/CharSequencesTest.java new file mode 100644 --- /dev/null +++ b/openide.util/test/unit/src/org/openide/util/CharSequencesTest.java @@ -0,0 +1,174 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.openide.util; + +import org.netbeans.junit.NbTestCase; +import java.util.Comparator; +import org.junit.Test; + +/** + * + * @author Vladimir Voskresensky + */ +public class CharSequencesTest extends NbTestCase { + + public CharSequencesTest() { + super("CharSequencesTest"); + } + + /** + * Test of create method, of class CharSequences. + */ + @Test + public void testCreate_3args() { + char[] buf = new char[] {'H', 'e', 'l', 'l', 'o', ',', ' ', 'P', 'l', 'a', 't', 'f', 'o', 'r', 'm', '!'}; + for (int start = 0; start < buf.length; start++) { + for (int count = 1; count < buf.length - start; count++) { + CharSequence expResult = new String(buf, start, count); + CharSequence result = CharSequences.create(buf, start, count); + assertEquals("["+start+", " + count+"]", expResult, result.toString()); + } + } + } + + /** + * Test of create method, of class CharSequences. + */ + @Test + public void testCreate_CharSequence() { + String[] strs = new String[] { "", "1234567", "123456789012345", "12345678901234567890123", "123456789012345678901234" }; + for (String str : strs) { + assertEquals(str, CharSequences.create(str).toString()); + assertEquals(CharSequences.create(str).toString(), str); + assertEquals(CharSequences.create(str).toString(), CharSequences.create(str).toString()); + } + } + + /** + * Test of comparator method, of class CharSequences. + */ + @Test + public void testCaseSensitiveComparator() { + Comparator comparator = CharSequences.comparator(false); + String[] strs = new String[]{"", "1234567", "123456789012345", "12345678901234567890123", "123456789012345678901234"}; + for (String str : strs) { + assertEquals(0, comparator.compare(str, CharSequences.create(str))); + assertEquals(0, comparator.compare(CharSequences.create(str), str)); + assertEquals(0, comparator.compare(CharSequences.create(str), CharSequences.create(str))); + } + } + + /** + * Test of comparator method, of class CharSequences. + */ + @Test + public void testCaseInsensitiveComparator() { + Comparator comparator = CharSequences.comparator(true); + String[] strs = new String[]{"", "1234567", "123456789012345", "12345678901234567890123", "123456789012345678901234"}; + for (int i = 0; i < strs.length; i++) { + String str = strs[i]; + String upperStr = str.toUpperCase(); + String lowerStr = str.toLowerCase(); + assertEquals(0, comparator.compare(str, CharSequences.create(str))); + assertEquals(0, comparator.compare(CharSequences.create(str), str)); + assertEquals(0, comparator.compare(CharSequences.create(str), CharSequences.create(str))); + assertEquals(0, comparator.compare(upperStr, CharSequences.create(str))); + assertEquals(0, comparator.compare(CharSequences.create(str), upperStr)); + assertEquals(0, comparator.compare(CharSequences.create(upperStr), CharSequences.create(str))); + assertEquals(0, comparator.compare(lowerStr, CharSequences.create(str))); + assertEquals(0, comparator.compare(CharSequences.create(str), lowerStr)); + assertEquals(0, comparator.compare(CharSequences.create(str), CharSequences.create(lowerStr))); + } + } + + /** + * Test of empty method, of class CharSequences. + */ + @Test + public void testEmpty() { + assertEquals("", CharSequences.create("").toString()); + assertEquals(CharSequences.create("").toString(), ""); + assertEquals("", CharSequences.empty().toString()); + assertEquals(CharSequences.empty().toString(), ""); + assertSame(CharSequences.empty(), CharSequences.create("")); + } + + /** + * Test of isCompact method, of class CharSequences. + */ + @Test + public void testIsCompact() { + String[] strs = new String[]{"", "1234567", "123456789012345", "12345678901234567890123", "123456789012345678901234"}; + for (String str : strs) { + assertFalse(" string is compact but must not be", CharSequences.isCompact(str)); + assertTrue(" string is not compact but must be", CharSequences.isCompact(CharSequences.create(str))); + } + assertTrue(" empty string is not compact ", CharSequences.isCompact(CharSequences.empty())); + } + + /** + * Test of indexOf method, of class CharSequences. + */ + @Test + public void testIndexOf_CharSequence_CharSequence() { + CharSequence text = CharSequences.create("CharSequences"); + CharSequence seq = CharSequences.create("Sequence"); + assertEquals(4, CharSequences.indexOf(text, "Sequence")); + assertEquals(4, CharSequences.indexOf(text, seq)); + assertEquals(4, CharSequences.indexOf("CharSequences", "Sequence")); + assertEquals(4, CharSequences.indexOf("CharSequences", seq)); + assertEquals(-1, CharSequences.indexOf(text, "Sequens")); + } + + /** + * Test of indexOf method, of class CharSequences. + */ + @Test + public void testIndexOf_3args() { + CharSequence text = CharSequences.create("CharSequences"); + CharSequence seq = CharSequences.create("Sequence"); + assertEquals(4, CharSequences.indexOf(text, "Sequence", 2)); + assertEquals(4, CharSequences.indexOf(text, seq, 2)); + assertEquals(4, CharSequences.indexOf("CharSequences", "Sequence", 2)); + assertEquals(4, CharSequences.indexOf("CharSequences", seq, 2)); + assertEquals(-1, CharSequences.indexOf("CharSequences", seq, 5)); + } + +} \ No newline at end of file