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 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 1469220 2013-04-18 08:15:47Z bayard $
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             int index1 = thisStart;
193             int index2 = start;
194             int tmpLen = length;
195 
196             while (tmpLen-- > 0) {
197                 char c1 = cs.charAt(index1++);
198                 char c2 = substring.charAt(index2++);
199 
200                 if (c1 == c2) {
201                     continue;
202                 }
203 
204                 if (!ignoreCase) {
205                     return false;
206                 }
207 
208                 // The same check as in String.regionMatches():
209                 if (Character.toUpperCase(c1) != Character.toUpperCase(c2)
210                         && Character.toLowerCase(c1) != Character.toLowerCase(c2)) {
211                     return false;
212                 }
213             }
214 
215             return true;
216         }
217     }
218 }