001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.lib.editor.util;
043:
044: /**
045: * Utility methods related to character sequences.
046: *
047: * @author Miloslav Metelka
048: * @version 1.00
049: */
050:
051: public final class CharSequenceUtilities {
052:
053: private CharSequenceUtilities() {
054: // no instances
055: }
056:
057: /**
058: * Compute {@link String}-like hashcode over given {@link CharSequence}.
059: *
060: * @param text character sequence for which the hashcode is being computed.
061: * @return hashcode of the given character sequence.
062: */
063: public static int stringLikeHashCode(CharSequence text) {
064: int len = text.length();
065:
066: int h = 0;
067: for (int i = 0; i < len; i++) {
068: h = 31 * h + text.charAt(i);
069: }
070: return h;
071: }
072:
073: /**
074: * Compare character sequence to another object.
075: * The match is successful if the second object is a character sequence as well
076: * and both character sequences contain the same characters (or if both objects are null).
077: *
078: * @param text character sequence being compared to the given object.
079: * It may be <code>null</code>.
080: * @param o object to be compared to the character sequence.
081: * It may be <code>null</code>.
082: * @return true if both parameters are null or both are non-null
083: * and they contain the same text.
084: */
085: public static boolean equals(CharSequence text, Object o) {
086: if (text == o) {
087: return true;
088: }
089:
090: if (text != null && o instanceof CharSequence) { // both non-null
091: return textEquals(text, (CharSequence) o);
092: }
093: return false;
094: }
095:
096: /**
097: * Test whether whether the given character sequences
098: * represent the same text.
099: * <br>
100: * The match is successful if the contained characters
101: * of the two character sequences are the same.
102: *
103: * @param text1 first character sequence being compared.
104: * It must not be <code>null</code>.
105: * @param text2 second character sequence being compared.
106: * It must not be <code>null</code>.
107: * @return true if both parameters are equal in String-like manner.
108: */
109: public static boolean textEquals(CharSequence text1,
110: CharSequence text2) {
111: if (text1 == text2) {
112: return true;
113: }
114: int len = text1.length();
115: if (len == text2.length()) {
116: for (int i = len - 1; i >= 0; i--) {
117: if (text1.charAt(i) != text2.charAt(i)) {
118: return false;
119: }
120: }
121: return true;
122: }
123: return false;
124: }
125:
126: /**
127: * Create a string from the given character sequence by first creating
128: * a <code>StringBuilder</code> and appending the whole character sequence
129: * to it.
130: * <br>
131: * The method does not call <code>toString()</code> on the given character
132: * sequence.
133: *
134: * @param text character sequence for which the <code>String</code> form
135: * should be created.
136: * @return string representation of the character sequence.
137: */
138: public static String toString(CharSequence text) {
139: StringBuilder sb = new StringBuilder(text.length());
140: sb.append(text);
141: return sb.toString();
142: }
143:
144: /**
145: * Create string for the given portion of the character sequence.
146: *
147: * @param text non-null text.
148: * @param start >=0 and <text.length() index of the first character
149: * to be present in the returned string.
150: * @param end >=start and <text.length() index after the last character
151: * to be present in the returned string.
152: */
153: public static String toString(CharSequence text, int start, int end) {
154: checkIndexesValid(text, start, end);
155: StringBuilder sb = new StringBuilder(end - start);
156: sb.append(text, start, end);
157: return sb.toString();
158: }
159:
160: /**
161: * Append character sequence to the given string buffer.
162: * <br/>
163: * This method is no longer needed in JDK 1.5 where the implementation
164: * does not create an extra java.lang.String instance.
165: */
166: public static void append(StringBuffer sb, CharSequence text) {
167: sb.append(text); // Only assume to run on 1.5
168: }
169:
170: /**
171: * Append part of the character sequence to the given string buffer.
172: * <br/>
173: * This method is no longer needed in JDK 1.5 where the implementation
174: * of the same functionality is available in the StringBuffer directly.
175: */
176: public static void append(StringBuffer sb, CharSequence text,
177: int start, int end) {
178: checkIndexesValid(text, start, end);
179: while (start < end) {
180: sb.append(text.charAt(start++));
181: }
182: }
183:
184: /**
185: * Implementation of {@link String#indexOf(int)} for character sequences.
186: */
187: public static int indexOf(CharSequence text, int ch) {
188: return indexOf(text, ch, 0);
189: }
190:
191: /**
192: * Implementation of {@link String#indexOf(int,int)} for character sequences.
193: */
194: public static int indexOf(CharSequence text, int ch, int fromIndex) {
195: int length = text.length();
196: while (fromIndex < length) {
197: if (text.charAt(fromIndex) == ch) {
198: return fromIndex;
199: }
200: fromIndex++;
201: }
202: return -1;
203: }
204:
205: /**
206: * Implementation of {@link String#indexOf(String)} for character sequences.
207: */
208: public static int indexOf(CharSequence text, CharSequence seq) {
209: return indexOf(text, seq, 0);
210: }
211:
212: /**
213: * Implementation of {@link String#indexOf(String,int)} for character sequences.
214: */
215: public static int indexOf(CharSequence text, CharSequence seq,
216: int fromIndex) {
217: int textLength = text.length();
218: int seqLength = seq.length();
219: if (fromIndex >= textLength) {
220: return (seqLength == 0 ? textLength : -1);
221: }
222: if (fromIndex < 0) {
223: fromIndex = 0;
224: }
225: if (seqLength == 0) {
226: return fromIndex;
227: }
228:
229: char first = seq.charAt(0);
230: int max = textLength - seqLength;
231:
232: for (int i = fromIndex; i <= max; i++) {
233: // look for first character
234: if (text.charAt(i) != first) {
235: while (++i <= max && text.charAt(i) != first)
236: ;
237: }
238:
239: // found first character, now look at the rest of seq
240: if (i <= max) {
241: int j = i + 1;
242: int end = j + seqLength - 1;
243: for (int k = 1; j < end
244: && text.charAt(j) == seq.charAt(k); j++, k++)
245: ;
246: if (j == end) {
247: // found whole sequence
248: return i;
249: }
250: }
251: }
252: return -1;
253: }
254:
255: /**
256: * Implementation of {@link String#lastIndexOf(String)} for character sequences.
257: */
258: public static int lastIndexOf(CharSequence text, CharSequence seq) {
259: return lastIndexOf(text, seq, text.length());
260: }
261:
262: /**
263: * Implementation of {@link String#lastIndexOf(String,int)} for character sequences.
264: */
265: public static int lastIndexOf(CharSequence text, CharSequence seq,
266: int fromIndex) {
267: int textLength = text.length();
268: int seqLength = seq.length();
269: int rightIndex = textLength - seqLength;
270: if (fromIndex < 0) {
271: return -1;
272: }
273: if (fromIndex > rightIndex) {
274: fromIndex = rightIndex;
275: }
276: // empty string always matches
277: if (seqLength == 0) {
278: return fromIndex;
279: }
280:
281: int strLastIndex = seqLength - 1;
282: char strLastChar = seq.charAt(strLastIndex);
283: int min = seqLength - 1;
284: int i = min + fromIndex;
285:
286: startSearchForLastChar: while (true) {
287: while (i >= min && text.charAt(i) != strLastChar) {
288: i--;
289: }
290:
291: if (i < min) {
292: return -1;
293: }
294: int j = i - 1;
295: int start = j - (seqLength - 1);
296: int k = strLastIndex - 1;
297:
298: while (j > start) {
299: if (text.charAt(j--) != seq.charAt(k--)) {
300: i--;
301: continue startSearchForLastChar;
302: }
303: }
304: return start + 1;
305: }
306: }
307:
308: /**
309: * Implementation of {@link String#lastIndexOf(int)} for character sequences.
310: */
311: public static int lastIndexOf(CharSequence text, int ch) {
312: return lastIndexOf(text, ch, text.length() - 1);
313: }
314:
315: /**
316: * Implementation of {@link String#lastIndexOf(int,int)} for character sequences.
317: */
318: public static int lastIndexOf(CharSequence text, int ch,
319: int fromIndex) {
320: if (fromIndex > text.length() - 1) {
321: fromIndex = text.length() - 1;
322: }
323: while (fromIndex >= 0) {
324: if (text.charAt(fromIndex) == ch) {
325: return fromIndex;
326: }
327: fromIndex--;
328: }
329: return -1;
330: }
331:
332: /**
333: * Implementation of {@link String#startsWith(String)} for character sequences.
334: */
335: public static boolean startsWith(CharSequence text,
336: CharSequence prefix) {
337: int p_length = prefix.length();
338: if (p_length > text.length()) {
339: return false;
340: }
341: for (int x = 0; x < p_length; x++) {
342: if (text.charAt(x) != prefix.charAt(x))
343: return false;
344: }
345: return true;
346: }
347:
348: /**
349: * Implementation of {@link String#endsWith(String)} for character sequences.
350: */
351: public static boolean endsWith(CharSequence text,
352: CharSequence suffix) {
353: int s_length = suffix.length();
354: int text_length = text.length();
355: if (s_length > text_length) {
356: return false;
357: }
358: for (int x = 0; x < s_length; x++) {
359: if (text.charAt(text_length - s_length + x) != suffix
360: .charAt(x))
361: return false;
362: }
363: return true;
364: }
365:
366: /**
367: * Implementation of {@link String#trim()} for character sequences.
368: */
369: public static CharSequence trim(CharSequence text) {
370: int length = text.length();
371: if (length == 0)
372: return text;
373: int start = 0;
374: int end = length - 1;
375: while (start < length && text.charAt(start) <= ' ') {
376: start++;
377: }
378: if (start == length)
379: return text.subSequence(0, 0);
380: while (end > start && text.charAt(end) <= ' ') {
381: end--;
382: }
383: return text.subSequence(start, end + 1);
384: }
385:
386: /**
387: * Append the character description to the given string buffer
388: * translating the special characters (and '\') into escape sequences.
389: *
390: * @param sb non-null string buffer to append to.
391: * @param ch character to be debugged.
392: */
393: public static void debugChar(StringBuffer sb, char ch) {
394: switch (ch) {
395: case '\n':
396: sb.append("\\n"); // NOI18N
397: break;
398: case '\r':
399: sb.append("\\r"); // NOI18N
400: break;
401: case '\t':
402: sb.append("\\t"); // NOI18N
403: break;
404: case '\b':
405: sb.append("\\b"); // NOI18N
406: break;
407: case '\f':
408: sb.append("\\f"); // NOI18N
409: break;
410: case '\\':
411: sb.append("\\\\"); // NOI18N
412: break;
413: default:
414: sb.append(ch);
415: break;
416: }
417: }
418:
419: /**
420: * Append the character description to the given string builder
421: * translating the special characters (and '\') into escape sequences.
422: *
423: * @param sb non-null string buffer to append to.
424: * @param ch character to be debugged.
425: */
426: public static void debugChar(StringBuilder sb, char ch) {
427: switch (ch) {
428: case '\n':
429: sb.append("\\n"); // NOI18N
430: break;
431: case '\r':
432: sb.append("\\r"); // NOI18N
433: break;
434: case '\t':
435: sb.append("\\t"); // NOI18N
436: break;
437: case '\b':
438: sb.append("\\b"); // NOI18N
439: break;
440: case '\f':
441: sb.append("\\f"); // NOI18N
442: break;
443: case '\\':
444: sb.append("\\\\"); // NOI18N
445: break;
446: default:
447: sb.append(ch);
448: break;
449: }
450: }
451:
452: /**
453: * Return the text description of the given character
454: * translating the special characters (and '\') into escape sequences.
455: *
456: * @param ch char to debug.
457: * @return non-null debug text.
458: */
459: public static String debugChar(char ch) {
460: StringBuilder sb = new StringBuilder();
461: debugChar(sb, ch);
462: return sb.toString();
463: }
464:
465: /**
466: * Append the text description to the given string buffer
467: * translating the special characters (and '\') into escape sequences.
468: *
469: * @param sb non-null string buffer to append to.
470: * @param text non-null text to be debugged.
471: */
472: public static void debugText(StringBuffer sb, CharSequence text) {
473: for (int i = 0; i < text.length(); i++) {
474: debugChar(sb, text.charAt(i));
475: }
476: }
477:
478: /**
479: * Append the text description to the given string builder
480: * translating the special characters (and '\') into escape sequences.
481: *
482: * @param sb non-null string builder to append to.
483: * @param text non-null text to be debugged.
484: */
485: public static void debugText(StringBuilder sb, CharSequence text) {
486: for (int i = 0; i < text.length(); i++) {
487: debugChar(sb, text.charAt(i));
488: }
489: }
490:
491: /**
492: * Create text description as String
493: * translating the special characters (and '\') into escape sequences.
494: *
495: * @param text non-null text to be debugged.
496: * @return non-null string containing the debug text.
497: */
498: public static String debugText(CharSequence text) {
499: StringBuilder sb = new StringBuilder();
500: debugText(sb, text);
501: return sb.toString();
502: }
503:
504: /**
505: * Ensure that the given index is >=0 and lower than the given length.
506: * @throws IndexOutOfBoundsException if the index is not within bounds.
507: */
508: public static void checkIndexNonNegative(int index) {
509: if (index < 0) {
510: throw new IndexOutOfBoundsException("index=" + index
511: + " < 0"); // NOI18N
512: }
513: }
514:
515: /**
516: * Ensure that the given index is >=0 and lower than the given length.
517: * @throws IndexOutOfBoundsException if the index is not within bounds.
518: */
519: public static void checkIndexValid(int index, int length) {
520: checkIndexNonNegative(index);
521: if (index >= length) {
522: throw new IndexOutOfBoundsException("index=" + index // NOI18N
523: + " >= length()=" + length); // NOI18N
524: }
525: }
526:
527: /**
528: * Ensure that the given start and end parameters are valid indices
529: * of the given text.
530: * @throws IndexOutOfBoundsException if the start or end are not within bounds
531: * of the given text.
532: */
533: public static void checkIndexesValid(CharSequence text, int start,
534: int end) {
535: if (start < 0) {
536: throw new IndexOutOfBoundsException("start=" + start
537: + " < 0"); // NOI18N
538: }
539: if (end < start) {
540: throw new IndexOutOfBoundsException("end=" + end
541: + " < start=" + start); // NOI18N
542: }
543: if (end > text.length()) {
544: throw new IndexOutOfBoundsException("end=" + end // NOI18N
545: + " > text.length()=" + text.length()); // NOI18N
546: }
547: }
548:
549: }
|