/* * Copyright 2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.grails.charsequences; import java.io.IOException; import java.io.Writer; /** * Utility functions for handling java.lang.CharSequence instances * * * @author Lari Hotari * @since 2.3.10 * */ public class CharSequences { private CharSequences() { } public static CharSequence createCharSequence(char[] chars) { return new CharArrayCharSequence(chars, 0, chars.length); } public static CharSequence createCharSequence(char[] chars, int start, int count) { return new CharArrayCharSequence(chars, start, count); } public static CharSequence createCharSequence(CharSequence str, int start, int count) { if(canUseOriginalForSubSequence(str, start, count)) { return str; } else { return new SubCharSequence(str, start, count); } } /** * Checks if start == 0 and count == length of CharSequence * It does this check only for String, StringBuilder and StringBuffer classes which have a fast way to check length * * Calculating length on GStringImpl requires building the result which is costly. * This helper method is to avoid calling length on other that String, StringBuilder and StringBuffer classes * when checking if the input CharSequence instance is already the same as the requested sub sequence * * @param str CharSequence input * @param start start index * @param count length on sub sequence * @return true if input is String, StringBuilder or StringBuffer class, start is 0 and count is length of input sequence */ public static boolean canUseOriginalForSubSequence(CharSequence str, int start, int count) { if (start != 0) return false; final Class<?> csqClass = str.getClass(); return (csqClass == String.class || csqClass == StringBuilder.class || csqClass == StringBuffer.class) && count == str.length(); } public static CharSequence createSingleCharSequence(int c) { return new SingleCharCharSequence(c); } public static CharSequence createSingleCharSequence(char ch) { return new SingleCharCharSequence(ch); } /** * Writes a CharSequence instance in the most optimal way to the target writer * * * @param target writer * @param csq source CharSequence instance * @param start start/offset index * @param end end index + 1 * @throws IOException */ public static void writeCharSequence(Writer target, CharSequence csq, int start, int end) throws IOException { final Class<?> csqClass = csq.getClass(); if (csqClass == String.class) { target.write((String)csq, start, end - start); } else if (csqClass == StringBuffer.class) { char[] buf = new char[end - start]; ((StringBuffer)csq).getChars(start, end, buf, 0); target.write(buf); } else if (csqClass == StringBuilder.class) { char[] buf = new char[end - start]; ((StringBuilder)csq).getChars(start, end, buf, 0); target.write(buf); } else if (csq instanceof CharArrayAccessible) { char[] buf = new char[end - start]; ((CharArrayAccessible)csq).getChars(start, end, buf, 0); target.write(buf); } else { String str = csq.subSequence(start, end).toString(); target.write(str, 0, str.length()); } } public static void writeCharSequence(Writer target, CharSequence csq) throws IOException { writeCharSequence(target, csq, 0, csq.length()); } /** * Provides an optimized way to copy CharSequence content to target array. * Uses getChars method available on String, StringBuilder and StringBuffer classes. * * Characters are copied from the source sequence <code>csq</code> into the * destination character array <code>dst</code>. The first character to * be copied is at index <code>srcBegin</code>; the last character to * be copied is at index <code>srcEnd-1</code>. The total number of * characters to be copied is <code>srcEnd-srcBegin</code>. The * characters are copied into the subarray of <code>dst</code> starting * at index <code>dstBegin</code> and ending at index: * <p><blockquote><pre> * dstbegin + (srcEnd-srcBegin) - 1 * </pre></blockquote> * * @param csq the source CharSequence instance. * @param srcBegin start copying at this offset. * @param srcEnd stop copying at this offset. * @param dst the array to copy the data into. * @param dstBegin offset into <code>dst</code>. * @throws NullPointerException if <code>dst</code> is * <code>null</code>. * @throws IndexOutOfBoundsException if any of the following is true: * <ul> * <li><code>srcBegin</code> is negative * <li><code>dstBegin</code> is negative * <li>the <code>srcBegin</code> argument is greater than * the <code>srcEnd</code> argument. * <li><code>srcEnd</code> is greater than * <code>this.length()</code>. * <li><code>dstBegin+srcEnd-srcBegin</code> is greater than * <code>dst.length</code> * </ul> */ public static void getChars(CharSequence csq, int srcBegin, int srcEnd, char dst[], int dstBegin) { final Class<?> csqClass = csq.getClass(); if (csqClass == String.class) { ((String)csq).getChars(srcBegin, srcEnd, dst, dstBegin); } else if (csqClass == StringBuffer.class) { ((StringBuffer)csq).getChars(srcBegin, srcEnd, dst, dstBegin); } else if (csqClass == StringBuilder.class) { ((StringBuilder)csq).getChars(srcBegin, srcEnd, dst, dstBegin); } else if (csq instanceof CharArrayAccessible) { ((CharArrayAccessible)csq).getChars(srcBegin, srcEnd, dst, dstBegin); } else { String str = csq.subSequence(srcBegin, srcEnd).toString(); str.getChars(0, str.length(), dst, dstBegin); } } }