    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.lang3;
    018
    019    /**
    020     * <p>Operations on {@link java.lang.CharSequence} that are
    021     * {@code null} safe.</p>
    022     *
    023     * @see java.lang.CharSequence
    024     * @since 3.0
    025     * @version $Id: CharSequenceUtils.java 1199894 2011-11-09 17:53:59Z ggregory $
    026     */
    027    public class CharSequenceUtils {
    028
    029        /**
    030         * <p>{@code CharSequenceUtils} instances should NOT be constructed in
    031         * standard programming. </p>
    032         *
    033         * <p>This constructor is public to permit tools that require a JavaBean
    034         * instance to operate.</p>
    035         */
    036        public CharSequenceUtils() {
    037            super();
    038        }
    039
    040        //-----------------------------------------------------------------------
    041        /**
    042         * <p>Returns a new {@code CharSequence} that is a subsequence of this
    043         * sequence starting with the {@code char} value at the specified index.</p>
    044         *
    045         * <p>This provides the {@code CharSequence} equivalent to {@link String#substring(int)}.
    046         * The length (in {@code char}) of the returned sequence is {@code length() - start},
    047         * so if {@code start == end} then an empty sequence is returned.</p>
    048         *
    049         * @param cs  the specified subsequence, null returns null
    050         * @param start  the start index, inclusive, valid
    051         * @return a new subsequence, may be null
    052         * @throws IndexOutOfBoundsException if {@code start} is negative or if
    053         *  {@code start} is greater than {@code length()}
    054         */
    055        public static CharSequence subSequence(CharSequence cs, int start) {
    056            return cs == null ? null : cs.subSequence(start, cs.length());
    057        }
    058
    059        //-----------------------------------------------------------------------
    060        /**
    061         * <p>Finds the first index in the {@code CharSequence} that matches the
    062         * specified character.</p>
    063         *
    064         * @param cs  the {@code CharSequence} to be processed, not null
    065         * @param searchChar  the char to be searched for
    066         * @param start  the start index, negative starts at the string start
    067         * @return the index where the search char was found, -1 if not found
    068         */
    069        static int indexOf(CharSequence cs, int searchChar, int start) {
    070            if (cs instanceof String) {
    071                return ((String) cs).indexOf(searchChar, start);
    072            } else {
    073                int sz = cs.length();
    074                if (start < 0) {
    075                    start = 0;
    076                }
    077                for (int i = start; i < sz; i++) {
    078                    if (cs.charAt(i) == searchChar) {
    079                        return i;
    080                    }
    081                }
    082                return -1;
    083            }
    084        }
    085
    086        /**
    087         * Used by the indexOf(CharSequence methods) as a green implementation of indexOf.
    088         *
    089         * @param cs the {@code CharSequence} to be processed
    090         * @param searchChar the {@code CharSequence} to be searched for
    091         * @param start the start index
    092         * @return the index where the search sequence was found
    093         */
    094        static int indexOf(CharSequence cs, CharSequence searchChar, int start) {
    095            return cs.toString().indexOf(searchChar.toString(), start);
    096    //        if (cs instanceof String && searchChar instanceof String) {
    097    //            // TODO: Do we assume searchChar is usually relatively small;
    098    //            //       If so then calling toString() on it is better than reverting to
    099    //            //       the green implementation in the else block
    100    //            return ((String) cs).indexOf((String) searchChar, start);
    101    //        } else {
    102    //            // TODO: Implement rather than convert to String
    103    //            return cs.toString().indexOf(searchChar.toString(), start);
    104    //        }
    105        }
    106
    107        /**
    108         * <p>Finds the last index in the {@code CharSequence} that matches the
    109         * specified character.</p>
    110         *
    111         * @param cs  the {@code CharSequence} to be processed
    112         * @param searchChar  the char to be searched for
    113         * @param start  the start index, negative returns -1, beyond length starts at end
    114         * @return the index where the search char was found, -1 if not found
    115         */
    116        static int lastIndexOf(CharSequence cs, int searchChar, int start) {
    117            if (cs instanceof String) {
    118                return ((String) cs).lastIndexOf(searchChar, start);
    119            } else {
    120                int sz = cs.length();
    121                if (start < 0) {
    122                    return -1;
    123                }
    124                if (start >= sz) {
    125                    start = sz - 1;
    126                }
    127                for (int i = start; i >= 0; --i) {
    128                    if (cs.charAt(i) == searchChar) {
    129                        return i;
    130                    }
    131                }
    132                return -1;
    133            }
    134        }
    135
    136        /**
    137         * Used by the lastIndexOf(CharSequence methods) as a green implementation of lastIndexOf
    138         *
    139         * @param cs the {@code CharSequence} to be processed
    140         * @param searchChar the {@code CharSequence} to be searched for
    141         * @param start the start index
    142         * @return the index where the search sequence was found
    143         */
    144        static int lastIndexOf(CharSequence cs, CharSequence searchChar, int start) {
    145            return cs.toString().lastIndexOf(searchChar.toString(), start);
    146    //        if (cs instanceof String && searchChar instanceof String) {
    147    //            // TODO: Do we assume searchChar is usually relatively small;
    148    //            //       If so then calling toString() on it is better than reverting to
    149    //            //       the green implementation in the else block
    150    //            return ((String) cs).lastIndexOf((String) searchChar, start);
    151    //        } else {
    152    //            // TODO: Implement rather than convert to String
    153    //            return cs.toString().lastIndexOf(searchChar.toString(), start);
    154    //        }
    155        }
    156
    157        /**
    158         * Green implementation of toCharArray.
    159         *
    160         * @param cs the {@code CharSequence} to be processed
    161         * @return the resulting char array
    162         */
    163        static char[] toCharArray(CharSequence cs) {
    164            if (cs instanceof String) {
    165                return ((String) cs).toCharArray();
    166            } else {
    167                int sz = cs.length();
    168                char[] array = new char[cs.length()];
    169                for (int i = 0; i < sz; i++) {
    170                    array[i] = cs.charAt(i);
    171                }
    172                return array;
    173            }
    174        }
    175
    176        /**
    177         * Green implementation of regionMatches.
    178         *
    179         * @param cs the {@code CharSequence} to be processed
    180         * @param ignoreCase whether or not to be case insensitive
    181         * @param thisStart the index to start on the {@code cs} CharSequence
    182         * @param substring the {@code CharSequence} to be looked for
    183         * @param start the index to start on the {@code substring} CharSequence
    184         * @param length character length of the region
    185         * @return whether the region matched
    186         */
    187        static boolean regionMatches(CharSequence cs, boolean ignoreCase, int thisStart,
    188                CharSequence substring, int start, int length)    {
    189            if (cs instanceof String && substring instanceof String) {
    190                return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length);
    191            } else {
    192                // TODO: Implement rather than convert to String
    193                return cs.toString().regionMatches(ignoreCase, thisStart, substring.toString(), start, length);
    194            }
    195        }
    196
    197    }