1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.lang3;
18
19 /**
20 * <p>Operations on {@link java.lang.CharSequence} that are
21 * {@code null} safe.</p>
22 *
23 * @see java.lang.CharSequence
24 * @since 3.0
25 * @version $Id: CharSequenceUtils.java 1436770 2013-01-22 07:09:45Z ggregory $
26 */
27 public class CharSequenceUtils {
28
29 /**
30 * <p>{@code CharSequenceUtils} instances should NOT be constructed in
31 * standard programming. </p>
32 *
33 * <p>This constructor is public to permit tools that require a JavaBean
34 * instance to operate.</p>
35 */
36 public CharSequenceUtils() {
37 super();
38 }
39
40 //-----------------------------------------------------------------------
41 /**
42 * <p>Returns a new {@code CharSequence} that is a subsequence of this
43 * sequence starting with the {@code char} value at the specified index.</p>
44 *
45 * <p>This provides the {@code CharSequence} equivalent to {@link String#substring(int)}.
46 * The length (in {@code char}) of the returned sequence is {@code length() - start},
47 * so if {@code start == end} then an empty sequence is returned.</p>
48 *
49 * @param cs the specified subsequence, null returns null
50 * @param start the start index, inclusive, valid
51 * @return a new subsequence, may be null
52 * @throws IndexOutOfBoundsException if {@code start} is negative or if
53 * {@code start} is greater than {@code length()}
54 */
55 public static CharSequence subSequence(final CharSequence cs, final int start) {
56 return cs == null ? null : cs.subSequence(start, cs.length());
57 }
58
59 //-----------------------------------------------------------------------
60 /**
61 * <p>Finds the first index in the {@code CharSequence} that matches the
62 * specified character.</p>
63 *
64 * @param cs the {@code CharSequence} to be processed, not null
65 * @param searchChar the char to be searched for
66 * @param start the start index, negative starts at the string start
67 * @return the index where the search char was found, -1 if not found
68 */
69 static int indexOf(final CharSequence cs, final int searchChar, int start) {
70 if (cs instanceof String) {
71 return ((String) cs).indexOf(searchChar, start);
72 } else {
73 final int sz = cs.length();
74 if (start < 0) {
75 start = 0;
76 }
77 for (int i = start; i < sz; i++) {
78 if (cs.charAt(i) == searchChar) {
79 return i;
80 }
81 }
82 return -1;
83 }
84 }
85
86 /**
87 * Used by the indexOf(CharSequence methods) as a green implementation of indexOf.
88 *
89 * @param cs the {@code CharSequence} to be processed
90 * @param searchChar the {@code CharSequence} to be searched for
91 * @param start the start index
92 * @return the index where the search sequence was found
93 */
94 static int indexOf(final CharSequence cs, final CharSequence searchChar, final int start) {
95 return cs.toString().indexOf(searchChar.toString(), start);
96 // if (cs instanceof String && searchChar instanceof String) {
97 // // TODO: Do we assume searchChar is usually relatively small;
98 // // If so then calling toString() on it is better than reverting to
99 // // 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(final CharSequence cs, final int searchChar, int start) {
117 if (cs instanceof String) {
118 return ((String) cs).lastIndexOf(searchChar, start);
119 } else {
120 final 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(final CharSequence cs, final CharSequence searchChar, final 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(final CharSequence cs) {
164 if (cs instanceof String) {
165 return ((String) cs).toCharArray();
166 } else {
167 final int sz = cs.length();
168 final 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(final CharSequence cs, final boolean ignoreCase, final int thisStart,
188 final CharSequence substring, final int start, final 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 }