001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: /*
011: * @(#)StringBuffer.java 1.28 96/02/14
012: *
013: * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
014: *
015: * Permission to use, copy, modify, and distribute this software
016: * and its documentation for NON-COMMERCIAL purposes and without
017: * fee is hereby granted provided that this copyright notice
018: * appears in all copies. Please refer to the file "copyright.html"
019: * for further important copyright and licensing information.
020: *
021: * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
022: * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
023: * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
024: * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
025: * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
026: * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
027: */
028:
029: package org.mmbase.util;
030:
031: /**
032: * This Class is a growable buffer for characters.
033: * It is mainly used to create Strings. The compiler uses it to implement the "+" operator.
034: * For example:
035: * <pre>
036: * "a" + 4 + "c"
037: * </pre>
038: * is compiled to:
039: * <pre>
040: * new StringBuffer().append("a").append(4).append("c").toString()
041: * </pre>
042: *
043: * Note that the method toString() does not create a copy of the internal buffer. Instead
044: * the buffer is marked as shared. Any further changes to the buffer will
045: * cause a copy to be made. <p>
046: *
047: * this is based on StringBuffer code, we have a seperate class since sun doesn't
048: * allow us to extend StringBuffer for some reason and we want methods like replace
049: * over the whole buffer.
050: *
051: * @license Sun license
052: * @see String
053: * @author Daniel Ockeloen
054: * @author Johannes Verelst (bugfix)
055: * @author Arthur van Hoff
056: * @version $Id: StringObject.java,v 1.11 2006/06/26 18:16:01 johannes Exp $
057: */
058:
059: public final class StringObject {
060: /** The value is used for character storage. */
061: private char value[];
062:
063: /** The count is the number of characters in the buffer. */
064: private int count;
065:
066: /** A flag indicating whether the buffer is shared */
067: private boolean shared;
068:
069: /**
070: * Constructs an empty String buffer.
071: */
072: public StringObject() {
073: this (16);
074: }
075:
076: /**
077: * Constructs an empty String buffer with the specified initial length.
078: * @param length the initial length
079: */
080: public StringObject(int length) {
081: value = new char[length];
082: shared = false;
083: }
084:
085: /**
086: * Constructs a String buffer with the specified initial value.
087: * @param str the initial value of the buffer
088: */
089: public StringObject(String str) {
090: this (str.length() + 16);
091: append(str);
092: }
093:
094: /**
095: * Returns the length (character count) of the buffer.
096: */
097: public int length() {
098: return count;
099: }
100:
101: /**
102: * Returns the current capacity of the String buffer. The capacity
103: * is the amount of storage available for newly inserted
104: * characters; beyond which an allocation will occur.
105: */
106: public int capacity() {
107: return value.length;
108: }
109:
110: /**
111: * Copies the buffer value if it is shared.
112: */
113: private final void copyWhenShared() {
114: if (shared) {
115: char newValue[] = new char[value.length];
116: System.arraycopy(value, 0, newValue, 0, count);
117: value = newValue;
118: shared = false;
119: }
120: }
121:
122: /**
123: * Ensures that the capacity of the buffer is at least equal to the
124: * specified minimum.
125: * @param minimumCapacity the minimum desired capacity
126: */
127: public synchronized void ensureCapacity(int minimumCapacity) {
128: int maxCapacity = value.length;
129:
130: if (minimumCapacity > maxCapacity) {
131: int newCapacity = (maxCapacity + 1) * 2;
132: if (minimumCapacity > newCapacity) {
133: newCapacity = minimumCapacity;
134: }
135:
136: char newValue[] = new char[newCapacity];
137: System.arraycopy(value, 0, newValue, 0, count);
138: value = newValue;
139: shared = false;
140: }
141: }
142:
143: /**
144: * Sets the length of the String. If the length is reduced, characters
145: * are lost. If the length is extended, the values of the new characters
146: * are set to 0.
147: * @param newLength the new length of the buffer
148: * @exception StringIndexOutOfBoundsException If the length is invalid.
149: */
150: public synchronized void setLength(int newLength) {
151: if (newLength < 0) {
152: throw new StringIndexOutOfBoundsException(newLength);
153: }
154: ensureCapacity(newLength);
155:
156: if (count < newLength) {
157: copyWhenShared();
158: for (; count < newLength; count++) {
159: value[count] = '\0';
160: }
161: }
162: count = newLength;
163: }
164:
165: /**
166: * Returns the character at the specified index. An index ranges
167: * from 0..length()-1.
168: * @param index the index of the desired character
169: * @exception StringIndexOutOfBoundsException If the index is invalid.
170: */
171: public synchronized char charAt(int index) {
172: if ((index < 0) || (index >= count)) {
173: throw new StringIndexOutOfBoundsException(index);
174: }
175: return value[index];
176: }
177:
178: /**
179: * Copies the characters of the specified substring (determined by
180: * srcBegin and srcEnd) into the character array, starting at the
181: * array's dstBegin location. Both srcBegin and srcEnd must be legal
182: * indexes into the buffer.
183: * @param srcBegin begin copy at this offset in the String
184: * @param srcEnd stop copying at this offset in the String
185: * @param dst the array to copy the data into
186: * @param dstBegin offset into dst
187: * @exception StringIndexOutOfBoundsException If there is an invalid index into the buffer.
188: */
189: public synchronized void getChars(int srcBegin, int srcEnd,
190: char dst[], int dstBegin) {
191: if ((srcBegin < 0) || (srcBegin >= count)) {
192: throw new StringIndexOutOfBoundsException(srcBegin);
193: }
194: if ((srcEnd < 0) || (srcEnd > count)) {
195: throw new StringIndexOutOfBoundsException(srcEnd);
196: }
197: if (srcBegin < srcEnd) {
198: System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd
199: - srcBegin);
200: }
201: }
202:
203: /**
204: * Changes the character at the specified index to be ch.
205: * @param index the index of the character
206: * @param ch the new character
207: * @exception StringIndexOutOfBoundsException If the index is invalid.
208: */
209: public synchronized void setCharAt(int index, char ch) {
210: if ((index < 0) || (index >= count)) {
211: throw new StringIndexOutOfBoundsException(index);
212: }
213: copyWhenShared();
214: value[index] = ch;
215: }
216:
217: /**
218: * Appends an object to the end of this buffer.
219: * @param obj the object to be appended
220: * @return the StringBuffer itself, NOT a new one.
221: */
222: public synchronized StringObject append(Object obj) {
223: return append(String.valueOf(obj));
224: }
225:
226: /**
227: * Appends a String to the end of this buffer.
228: * @param str the String to be appended
229: * @return the StringBuffer itself, NOT a new one.
230: */
231: public synchronized StringObject append(String str) {
232: if (str == null) {
233: str = String.valueOf(str);
234: }
235:
236: int len = str.length();
237: ensureCapacity(count + len);
238: copyWhenShared();
239: str.getChars(0, len, value, count);
240: count += len;
241: return this ;
242: }
243:
244: /**
245: * Appends an array of characters to the end of this buffer.
246: * @param str the characters to be appended
247: * @return the StringBuffer itself, NOT a new one.
248: */
249: public synchronized StringObject append(char str[]) {
250: int len = str.length;
251: ensureCapacity(count + len);
252: copyWhenShared();
253: System.arraycopy(str, 0, value, count, len);
254: count += len;
255: return this ;
256: }
257:
258: /**
259: * Appends a part of an array of characters to the end of this buffer.
260: * @param str the characters to be appended
261: * @param offset where to start
262: * @param len the number of characters to add
263: * @return the StringBuffer itself, NOT a new one.
264: */
265: public synchronized StringObject append(char str[], int offset,
266: int len) {
267: ensureCapacity(count + len);
268: copyWhenShared();
269: System.arraycopy(str, offset, value, count, len);
270: count += len;
271: return this ;
272: }
273:
274: /**
275: * Appends a boolean to the end of this buffer.
276: * @param b the boolean to be appended
277: * @return the StringBuffer itself, NOT a new one.
278: */
279: public StringObject append(boolean b) {
280: return append(String.valueOf(b));
281: }
282:
283: /**
284: * Appends a character to the end of this buffer.
285: * @param c the character to be appended
286: * @return the StringBuffer itself, NOT a new one.
287: */
288: public synchronized StringObject append(char c) {
289: ensureCapacity(count + 1);
290: copyWhenShared();
291: value[count++] = c;
292: return this ;
293: }
294:
295: /**
296: * Appends an integer to the end of this buffer.
297: * @param i the integer to be appended
298: * @return the StringBuffer itself, NOT a new one.
299: */
300: public StringObject append(int i) {
301: return append(String.valueOf(i));
302: }
303:
304: /**
305: * Appends a long to the end of this buffer.
306: * @param l the long to be appended
307: * @return the StringBuffer itself, NOT a new one.
308: */
309: public StringObject append(long l) {
310: return append(String.valueOf(l));
311: }
312:
313: /**
314: * Appends a float to the end of this buffer.
315: * @param f the float to be appended
316: * @return the StringBuffer itself, NOT a new one.
317: */
318: public StringObject append(float f) {
319: return append(String.valueOf(f));
320: }
321:
322: /**
323: * Appends a double to the end of this buffer.
324: * @param d the double to be appended
325: * @return the StringBuffer itself, NOT a new one.
326: */
327: public StringObject append(double d) {
328: return append(String.valueOf(d));
329: }
330:
331: /**
332: * Inserts an object into the String buffer.
333: * @param offset the offset at which to insert
334: * @param obj the object to insert
335: * @return the StringBuffer itself, NOT a new one.
336: * @exception StringIndexOutOfBoundsException If the offset is invalid.
337: */
338: public synchronized StringObject insert(int offset, Object obj) {
339: return insert(offset, String.valueOf(obj));
340: }
341:
342: /**
343: * Inserts a String into the String buffer.
344: * @param offset the offset at which to insert
345: * @param str the String to insert
346: * @return the StringBuffer itself, NOT a new one.
347: * @exception StringIndexOutOfBoundsException If the offset is invalid.
348: */
349: public synchronized StringObject insert(int offset, String str) {
350: if ((offset < 0) || (offset > count)) {
351: throw new StringIndexOutOfBoundsException();
352: }
353: int len = str.length();
354: ensureCapacity(count + len);
355: copyWhenShared();
356: System.arraycopy(value, offset, value, offset + len, count
357: - offset);
358: str.getChars(0, len, value, offset);
359: count += len;
360: return this ;
361: }
362:
363: /**
364: * Inserts an array of characters into the String buffer.
365: * @param offset the offset at which to insert
366: * @param str the characters to insert
367: * @return the StringBuffer itself, NOT a new one.
368: * @exception StringIndexOutOfBoundsException If the offset is invalid.
369: */
370: public synchronized StringObject insert(int offset, char str[]) {
371: if ((offset < 0) || (offset > count)) {
372: throw new StringIndexOutOfBoundsException();
373: }
374: int len = str.length;
375: ensureCapacity(count + len);
376: copyWhenShared();
377: System.arraycopy(value, offset, value, offset + len, count
378: - offset);
379: System.arraycopy(str, 0, value, offset, len);
380: count += len;
381: return this ;
382: }
383:
384: /**
385: * Inserts a boolean into the String buffer.
386: * @param offset the offset at which to insert
387: * @param b the boolean to insert
388: * @return the StringBuffer itself, NOT a new one.
389: * @exception StringIndexOutOfBoundsException If the offset is invalid.
390: */
391: public StringObject insert(int offset, boolean b) {
392: return insert(offset, String.valueOf(b));
393: }
394:
395: /**
396: * Inserts a character into the String buffer.
397: * @param offset the offset at which to insert
398: * @param c the character to insert
399: * @return the StringBuffer itself, NOT a new one.
400: * @exception StringIndexOutOfBoundsException If the offset invalid.
401: */
402: public synchronized StringObject insert(int offset, char c) {
403: ensureCapacity(count + 1);
404: copyWhenShared();
405: System.arraycopy(value, offset, value, offset + 1, count
406: - offset);
407: value[offset] = c;
408: count += 1;
409: return this ;
410: }
411:
412: /**
413: * Inserts an integer into the String buffer.
414: * @param offset the offset at which to insert
415: * @param i the integer to insert
416: * @return the StringBuffer itself, NOT a new one.
417: * @exception StringIndexOutOfBoundsException If the offset is invalid.
418: */
419: public StringObject insert(int offset, int i) {
420: return insert(offset, String.valueOf(i));
421: }
422:
423: /**
424: * Inserts a long into the String buffer.
425: * @param offset the offset at which to insert
426: * @param l the long to insert
427: * @return the StringBuffer itself, NOT a new one.
428: * @exception StringIndexOutOfBoundsException If the offset is invalid.
429: */
430: public StringObject insert(int offset, long l) {
431: return insert(offset, String.valueOf(l));
432: }
433:
434: /**
435: * Inserts a float into the String buffer.
436: * @param offset the offset at which to insert
437: * @param f the float to insert
438: * @return the StringBuffer itself, NOT a new one.
439: * @exception StringIndexOutOfBoundsException If the offset is invalid.
440: */
441: public StringObject insert(int offset, float f) {
442: return insert(offset, String.valueOf(f));
443: }
444:
445: /**
446: * Inserts a double into the String buffer.
447: * @param offset the offset at which to insert
448: * @param d the double to insert
449: * @return the StringBuffer itself, NOT a new one.
450: * @exception StringIndexOutOfBoundsException If the offset is invalid.
451: */
452: public StringObject insert(int offset, double d) {
453: return insert(offset, String.valueOf(d));
454: }
455:
456: /**
457: * Reverse the order of the characters in the String buffer.
458: */
459: public synchronized StringObject reverse() {
460: copyWhenShared();
461: int n = count - 1;
462: for (int j = (n - 1) >> 1; j >= 0; --j) {
463: char temp = value[j];
464: value[j] = value[n - j];
465: value[n - j] = temp;
466: }
467: return this ;
468: }
469:
470: /**
471: * Converts to a String representing the data in the buffer.
472: */
473: public String toString() {
474: return new String(value, 0, count);
475: }
476:
477: // The following two methods are needed by String to efficiently
478: // convert a StringBuffer into a String. They are not public.
479: // They shouldn't be called by anyone but String.
480: // XXX doesn't make sense... if this is package scope how can String call it?
481:
482: final void setShared() {
483: shared = true;
484: }
485:
486: final char[] getValue() {
487: return value;
488: }
489:
490: /**
491: * delete part of the buffer
492: */
493: public synchronized StringObject delete(int offset, int len) {
494:
495: if ((offset < 0) || (offset > count)) {
496: throw new StringIndexOutOfBoundsException();
497: }
498: copyWhenShared();
499: System.arraycopy(value, offset + len, value, offset, count
500: - (offset + len));
501: count -= len;
502: return this ;
503: }
504:
505: /**
506: * replace
507: */
508: public synchronized StringObject replace(int offset, int len,
509: String str) {
510: delete(offset, len);
511: insert(offset, str);
512: return this ;
513: }
514:
515: /**
516: * replace
517: */
518: public synchronized StringObject replaceFirst(String oldstr,
519: String newstr) {
520: int pos = indexOf(oldstr, 0);
521: if (pos != -1) {
522: delete(pos, oldstr.length());
523: insert(pos, newstr);
524: }
525: return this ;
526: }
527:
528: /**
529: * replace
530: */
531: public synchronized StringObject replace(String oldstr,
532: String newstr) {
533: int strlen = oldstr.length();
534: int pos = indexOf(oldstr, 0, strlen);
535: while (pos != -1) {
536: delete(pos, strlen);
537: insert(pos, newstr);
538: pos = indexOf(oldstr, pos + newstr.length(), strlen);
539: }
540: return this ;
541: }
542:
543: /**
544: * Does a replace/insert.
545: * Like make bold:bla into <:b>:bla<:/b>:
546: */
547: public synchronized StringObject replace(String oldstart,
548: String oldend, String newstart, String newend) {
549: int pos2;
550: int pos = indexOf(oldstart, 0);
551:
552: while (pos != -1) {
553: delete(pos, oldstart.length());
554: insert(pos, newstart);
555: pos2 = indexOf(oldend, pos + newstart.length());
556: if (pos2 != -1) {
557: delete(pos2, oldend.length());
558: insert(pos2, newend);
559: }
560: pos = indexOf(oldstart, pos2 + newend.length());
561: }
562: return this ;
563: }
564:
565: /**
566: * inserts links
567: */
568: public synchronized StringObject insertLinks(String oldstart,
569: String oldend, String newstart, String newend,
570: String startend) {
571: int pos2;
572: int pos = indexOf(oldstart, 0);
573: String link = "";
574: while (pos != -1) {
575: delete(pos, oldstart.length());
576: insert(pos, newstart);
577: pos2 = indexOf(oldend, pos + newstart.length());
578: if (pos2 != -1) {
579: link = new String(value, pos + newstart.length(), pos2
580: - (pos + newstart.length()));
581: delete(pos2, oldend.length());
582: insert(pos2, newend + link + startend);
583: }
584: pos = indexOf(oldstart, pos2 + newend.length());
585: }
586: return this ;
587: }
588:
589: public int indexOf(String str) {
590: return indexOf(str, 0);
591: }
592:
593: public int indexOf(String str, int fromIndex) {
594: return indexOf(str, fromIndex, str.length());
595: }
596:
597: private int indexOf(String str, int fromIndex, int strlen) {
598: char v1[] = value;
599: char v2[] = str.toCharArray();
600: int max = (count - strlen);
601: test: for (int i = ((fromIndex < 0) ? 0 : fromIndex); i <= max; i++) {
602: int n = strlen;
603: int j = i;
604: int k = 0;
605: while (n-- != 0) {
606: if (v1[j++] != v2[k++]) {
607: continue test;
608: }
609: }
610: return i;
611: }
612: return -1;
613: }
614:
615: /**
616: */
617: public byte[] getBytes() {
618: return toString().getBytes();
619: }
620: }
|