View Javadoc
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 CharSequence} that are
21   * {@code null} safe.</p>
22   *
23   * @see CharSequence
24   * @since 3.0
25   * @version $Id$
26   */
27  public class CharSequenceUtils {
28  
29      private static final int NOT_FOUND = -1;
30  
31      /**
32       * <p>{@code CharSequenceUtils} instances should NOT be constructed in
33       * standard programming. </p>
34       *
35       * <p>This constructor is public to permit tools that require a JavaBean
36       * instance to operate.</p>
37       */
38      public CharSequenceUtils() {
39          super();
40      }
41  
42      //-----------------------------------------------------------------------
43      /**
44       * <p>Returns a new {@code CharSequence} that is a subsequence of this
45       * sequence starting with the {@code char} value at the specified index.</p>
46       *
47       * <p>This provides the {@code CharSequence} equivalent to {@link String#substring(int)}.
48       * The length (in {@code char}) of the returned sequence is {@code length() - start},
49       * so if {@code start == end} then an empty sequence is returned.</p>
50       *
51       * @param cs  the specified subsequence, null returns null
52       * @param start  the start index, inclusive, valid
53       * @return a new subsequence, may be null
54       * @throws IndexOutOfBoundsException if {@code start} is negative or if 
55       *  {@code start} is greater than {@code length()}
56       */
57      public static CharSequence subSequence(final CharSequence cs, final int start) {
58          return cs == null ? null : cs.subSequence(start, cs.length());
59      }
60  
61      //-----------------------------------------------------------------------
62      /**
63       * <p>Finds the first index in the {@code CharSequence} that matches the
64       * specified character.</p>
65       *
66       * @param cs  the {@code CharSequence} to be processed, not null
67       * @param searchChar  the char to be searched for
68       * @param start  the start index, negative starts at the string start
69       * @return the index where the search char was found, -1 if not found
70       */
71      static int indexOf(final CharSequence cs, final int searchChar, int start) {
72          if (cs instanceof String) {
73              return ((String) cs).indexOf(searchChar, start);
74          }
75          final int sz = cs.length();
76          if (start < 0) {
77              start = 0;
78          }
79          for (int i = start; i < sz; i++) {
80              if (cs.charAt(i) == searchChar) {
81                  return i;
82              }
83          }
84          return NOT_FOUND;
85      }
86  
87      /**
88       * Used by the indexOf(CharSequence methods) as a green implementation of indexOf.
89       *
90       * @param cs the {@code CharSequence} to be processed
91       * @param searchChar the {@code CharSequence} to be searched for
92       * @param start the start index
93       * @return the index where the search sequence was found
94       */
95      static int indexOf(final CharSequence cs, final CharSequence searchChar, final int start) {
96          return cs.toString().indexOf(searchChar.toString(), start);
97  //        if (cs instanceof String && searchChar instanceof String) {
98  //            // TODO: Do we assume searchChar is usually relatively small;
99  //            //       If so then calling toString() on it is better than reverting to
100 //            //       the green implementation in the else block
101 //            return ((String) cs).indexOf((String) searchChar, start);
102 //        } else {
103 //            // TODO: Implement rather than convert to String
104 //            return cs.toString().indexOf(searchChar.toString(), start);
105 //        }
106     }
107 
108     /**
109      * <p>Finds the last index in the {@code CharSequence} that matches the
110      * specified character.</p>
111      *
112      * @param cs  the {@code CharSequence} to be processed
113      * @param searchChar  the char to be searched for
114      * @param start  the start index, negative returns -1, beyond length starts at end
115      * @return the index where the search char was found, -1 if not found
116      */
117     static int lastIndexOf(final CharSequence cs, final int searchChar, int start) {
118         if (cs instanceof String) {
119             return ((String) cs).lastIndexOf(searchChar, start);
120         }
121         final int sz = cs.length();
122         if (start < 0) {
123             return NOT_FOUND;
124         }
125         if (start >= sz) {
126             start = sz - 1;
127         }
128         for (int i = start; i >= 0; --i) {
129             if (cs.charAt(i) == searchChar) {
130                 return i;
131             }
132         }
133         return NOT_FOUND;
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         }
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      * Green implementation of regionMatches.
177      *
178      * @param cs the {@code CharSequence} to be processed
179      * @param ignoreCase whether or not to be case insensitive
180      * @param thisStart the index to start on the {@code cs} CharSequence
181      * @param substring the {@code CharSequence} to be looked for
182      * @param start the index to start on the {@code substring} CharSequence
183      * @param length character length of the region
184      * @return whether the region matched
185      */
186     static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart,
187             final CharSequence substring, final int start, final int length)    {
188         if (cs instanceof String && substring instanceof String) {
189             return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length);
190         }
191         int index1 = thisStart;
192         int index2 = start;
193         int tmpLen = length;
194 
195         while (tmpLen-- > 0) {
196             final char c1 = cs.charAt(index1++);
197             final char c2 = substring.charAt(index2++);
198 
199             if (c1 == c2) {
200                 continue;
201             }
202 
203             if (!ignoreCase) {
204                 return false;
205             }
206 
207             // The same check as in String.regionMatches():
208             if (Character.toUpperCase(c1) != Character.toUpperCase(c2)
209                     && Character.toLowerCase(c1) != Character.toLowerCase(c2)) {
210                 return false;
211             }
212         }
213 
214         return true;
215     }
216 }