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 1091471 2011-04-12 15:34:43Z scolebourne $
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 }