001package com.hfg.util;
002
003import java.io.IOException;
004import java.io.OutputStream;
005
006//------------------------------------------------------------------------------
007/**
008 * A StringBuilder with extra functionality [delimtedAppend(), printf(), appendln(),
009 * replaceAll(), and carriage return support].
010 *
011 * @author J. Alex Taylor, hairyfatguy.com
012 */
013//------------------------------------------------------------------------------
014// com.hfg XML/HTML Coding Library
015//
016// This library is free software; you can redistribute it and/or
017// modify it under the terms of the GNU Lesser General Public
018// License as published by the Free Software Foundation; either
019// version 2.1 of the License, or (at your option) any later version.
020//
021// This library is distributed in the hope that it will be useful,
022// but WITHOUT ANY WARRANTY; without even the implied warranty of
023// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
024// Lesser General Public License for more details.
025//
026// You should have received a copy of the GNU Lesser General Public
027// License along with this library; if not, write to the Free Software
028// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
029//
030// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
031// jataylor@hairyfatguy.com
032//------------------------------------------------------------------------------
033
034// Can't extend StringBuilder since it is is a final class so we just have to wrap it.
035public class StringBuilderPlus extends OutputStream implements CharSequence
036{
037   private StringBuilder mBuffer;
038   private String        mDelimiter     = sDefaultDelimiter;
039   private String        mLineSeparator = sDefaultLineSeparator;
040   private boolean       mCarriageReturnEnabled;
041
042   private static String sDefaultDelimiter     = ", ";
043   private static String sDefaultLineSeparator = System.getProperty("line.separator");
044
045   //##########################################################################
046   // CONSTRUCTORS
047   //##########################################################################
048
049   //--------------------------------------------------------------------------
050   public StringBuilderPlus()
051   {
052      mBuffer = new StringBuilder();
053   }
054
055   //--------------------------------------------------------------------------
056   public StringBuilderPlus(int inSize)
057   {
058      mBuffer = new StringBuilder(inSize);
059   }
060
061   //--------------------------------------------------------------------------
062   public StringBuilderPlus(CharSequence inContent)
063   {
064      mBuffer = new StringBuilder();
065      if (inContent != null)
066      {
067         mBuffer.append(inContent);
068      }
069   }
070
071   //##########################################################################
072   // PUBLIC METHODS
073   //##########################################################################
074
075   //--------------------------------------------------------------------------
076   /**
077    Sets an override for the default line separator of System.getProperty("line.separator").
078    */
079   public static void setDefaultLineSeparator(String inValue)
080   {
081      sDefaultLineSeparator = inValue;
082   }
083
084   //--------------------------------------------------------------------------
085   public static void setDefaultDelimiter(String inValue)
086   {
087      sDefaultDelimiter = inValue;
088   }
089   
090
091   //--------------------------------------------------------------------------
092   public StringBuilderPlus setLineSeparator(String inValue)
093   {
094      mLineSeparator = inValue;
095
096      return this;
097   }
098
099   //--------------------------------------------------------------------------
100   public StringBuilderPlus setDelimiter(String inValue)
101   {
102      mDelimiter = inValue;
103
104      return this;
105   }
106
107   //--------------------------------------------------------------------------
108   public String getDelimiter()
109   {
110      return mDelimiter;
111   }
112
113   //--------------------------------------------------------------------------
114   public void moveCharAt(int inFromIndex, int intToIndex)
115   {
116      char charToMove = mBuffer.charAt(inFromIndex);
117      mBuffer.insert(inFromIndex < intToIndex ? intToIndex + 1 : intToIndex, charToMove);
118      mBuffer.deleteCharAt(inFromIndex < intToIndex ? inFromIndex : inFromIndex + 1);
119   }
120
121   //--------------------------------------------------------------------------
122   public StringBuilderPlus replaceAll(Character inTarget, Character inReplacement)
123   {
124      return replaceAll(inTarget + "", inReplacement != null ? inReplacement + "" : null);
125   }
126
127   //--------------------------------------------------------------------------
128   public StringBuilderPlus replaceAll(CharSequence inTarget, Character inReplacement)
129   {
130      return replaceAll(inTarget, inReplacement != null ? inReplacement + "" : null);
131   }
132
133   //--------------------------------------------------------------------------
134   public StringBuilderPlus replaceAll(Character inTarget, CharSequence inReplacement)
135   {
136      return replaceAll(inTarget + "", inReplacement);
137   }
138
139   //--------------------------------------------------------------------------
140   public StringBuilderPlus replaceAll(CharSequence inTarget, CharSequence inReplacement)
141   {
142      String replacement = inReplacement != null ? inReplacement + "" : "";
143
144      int index;
145      int fromIndex = 0;
146      while ((index = indexOf(inTarget + "", fromIndex)) >= 0)
147      {
148         replace(index, index + inTarget.length(), replacement);
149         fromIndex = index + replacement.length();
150      }
151
152      return this;
153   }
154
155   //--------------------------------------------------------------------------
156   /**
157    Causes any '\r' characters to be immediately interpreted as carriage returns.
158    * @return this StringBuilderPlus object to allow method chaining.
159    */
160   public StringBuilderPlus enableCarriageReturnSupport()
161   {
162      mCarriageReturnEnabled = true;
163
164      // Process any pre-existing carriage returns
165      if (length() > 0
166          && indexOf("\r") >= 0)
167      {
168         processAddedContentForCarriageReturns(toString());
169      }
170
171      return this;
172   }
173
174   //--------------------------------------------------------------------------
175   public StringBuilderPlus disableCarriageReturnSupport()
176   {
177      mCarriageReturnEnabled = false;
178      return this;
179   }
180
181   //--------------------------------------------------------------------------
182   public boolean startsWith(String inValue)
183   {
184      return toString().startsWith(inValue);
185   }
186
187   //--------------------------------------------------------------------------
188   public boolean endsWith(String inValue)
189   {
190      return toString().endsWith(inValue);
191   }
192
193   // OutputStream methods
194
195   //--------------------------------------------------------------------------
196   @Override
197   public void write(int inByte)
198         throws IOException
199   {
200      append((char) inByte);
201   }
202
203   //--------------------------------------------------------------------------
204   @Override
205   public void write(byte inBytes[], int inOffset, int inLength)
206   {
207      append(new String(inBytes, inOffset, inLength));
208   }
209
210   //--------------------------------------------------------------------------
211   @Override
212   public void write(byte inBytes[])
213   {
214      append(new String(inBytes));
215   }
216
217
218
219   //--------------------------------------------------------------------------
220   public StringBuilderPlus delimitedAppend(CharSequence inContent)
221   {
222      delimitIfNecessary();
223
224      append(inContent);
225
226      return this;
227   }
228
229   //--------------------------------------------------------------------------
230   public StringBuilderPlus delimitedAppend(char inContent)
231   {
232      delimitIfNecessary();
233
234      append(inContent);
235
236      return this;
237   }
238
239   //--------------------------------------------------------------------------
240   public StringBuilderPlus delimitedAppend(int inContent)
241   {
242      delimitIfNecessary();
243
244      append(inContent);
245
246      return this;
247   }
248
249   //--------------------------------------------------------------------------
250   public StringBuilderPlus delimitedAppend(long inContent)
251   {
252      delimitIfNecessary();
253
254      append(inContent);
255
256      return this;
257   }
258
259   //--------------------------------------------------------------------------
260   public StringBuilderPlus delimitedAppend(float inContent)
261   {
262      delimitIfNecessary();
263
264      append(inContent);
265
266      return this;
267   }
268
269   //--------------------------------------------------------------------------
270   public StringBuilderPlus delimitedAppend(double inContent)
271   {
272      delimitIfNecessary();
273
274      append(inContent);
275
276      return this;
277   }
278
279   //--------------------------------------------------------------------------
280   public StringBuilderPlus delimitedAppend(Object inContent)
281   {
282      delimitIfNecessary();
283
284      append(inContent);
285
286      return this;
287   }
288
289   //--------------------------------------------------------------------------
290   public StringBuilderPlus printf(String inFormatString, Object... inArgs)
291   {
292      return append(String.format(inFormatString, inArgs));
293   }
294
295   //--------------------------------------------------------------------------
296   public StringBuilderPlus appendln(CharSequence inContent)
297   {
298      append(inContent);
299      mBuffer.append(mLineSeparator);
300      return this;
301   }
302
303   //--------------------------------------------------------------------------
304   public StringBuilderPlus appendln()
305   {
306      mBuffer.append(mLineSeparator);
307
308      return this;
309   }
310
311   // Methods from StringBuilder:
312
313   //--------------------------------------------------------------------------
314   public StringBuilderPlus append(Object inObj)
315   {
316      mBuffer.append(inObj);
317
318      return this;
319   }
320
321   //--------------------------------------------------------------------------
322   public StringBuilderPlus append(CharSequence inContent)
323   {
324      CharSequence content = inContent;
325      if (mCarriageReturnEnabled
326            && inContent != null
327            && inContent.toString().contains("\r"))
328      {
329         content = processAddedContentForCarriageReturns(inContent);
330         if (content.charAt(0) == '\r')
331         {
332            int lastLineFeedIndex = lastIndexOf("\n");
333            replace(lastLineFeedIndex + 1, length(), "");
334            // Remove the '\r' from the beginning of the added content
335            content = content.subSequence(1, content.length());
336         }
337      }
338
339      mBuffer.append(content);
340
341      return this;
342   }
343
344   //--------------------------------------------------------------------------
345   public StringBuilderPlus append(CharSequence inContent, int inStart, int inEnd)
346   {
347      mBuffer.append(inContent, inStart, inEnd);
348
349      return this;
350   }
351
352   //--------------------------------------------------------------------------
353   public StringBuilderPlus append(char[] inContent)
354   {
355      mBuffer.append(inContent);
356
357      return this;
358   }
359
360   //--------------------------------------------------------------------------
361   public StringBuilderPlus append(char[] inContent, int inOffset, int inLen)
362   {
363      mBuffer.append(inContent, inOffset, inLen);
364
365      return this;
366   }
367
368   //--------------------------------------------------------------------------
369   public StringBuilderPlus append(boolean inContent)
370   {
371      mBuffer.append(inContent);
372
373      return this;
374   }
375
376   //--------------------------------------------------------------------------
377   public StringBuilderPlus append(char inContent)
378   {
379      if (mCarriageReturnEnabled
380            && inContent == '\r')
381      {
382         append(inContent + "");
383      }
384      else
385      {
386         mBuffer.append(inContent);
387      }
388
389      return this;
390   }
391
392   //--------------------------------------------------------------------------
393   public StringBuilderPlus append(int inContent)
394   {
395      mBuffer.append(inContent);
396
397      return this;
398   }
399
400   //--------------------------------------------------------------------------
401   public StringBuilderPlus append(long inContent)
402   {
403      mBuffer.append(inContent);
404
405      return this;
406   }
407
408   //--------------------------------------------------------------------------
409   public StringBuilderPlus append(float inContent)
410   {
411      mBuffer.append(inContent);
412
413      return this;
414   }
415
416   //--------------------------------------------------------------------------
417   public StringBuilderPlus append(double inContent)
418   {
419      mBuffer.append(inContent);
420
421      return this;
422   }
423
424   //--------------------------------------------------------------------------
425   public StringBuilderPlus appendCodePoint(int inContent)
426   {
427      mBuffer.appendCodePoint(inContent);
428
429      return this;
430   }
431
432   //--------------------------------------------------------------------------
433   public StringBuilderPlus delete(int inStart, int inEnd)
434   {
435      mBuffer.delete(inStart, inEnd);
436
437      return this;
438   }
439
440   //--------------------------------------------------------------------------
441   public StringBuilderPlus deleteCharAt(int inIndex)
442   {
443      mBuffer.deleteCharAt(inIndex);
444
445      return this;
446   }
447
448   //--------------------------------------------------------------------------
449   public StringBuilderPlus replace(int inStart, int inEnd, String inReplacement)
450   {
451      mBuffer.replace(inStart, inEnd, inReplacement);
452
453      return this;
454   }
455
456   //--------------------------------------------------------------------------
457   public StringBuilderPlus insert(int inIndex, char[] inContent)
458   {
459      mBuffer.insert(inIndex, inContent);
460
461      return this;
462   }
463
464   //--------------------------------------------------------------------------
465   public StringBuilderPlus insert(int inIndex, char[] inContent, int inOffset, int inLen)
466   {
467      mBuffer.insert(inIndex, inContent, inOffset, inLen);
468
469      return this;
470   }
471
472   //--------------------------------------------------------------------------
473   public StringBuilderPlus insert(int inIndex, Object inContent)
474   {
475      mBuffer.insert(inIndex, inContent);
476
477      return this;
478   }
479
480   //--------------------------------------------------------------------------
481   public StringBuilderPlus insert(int inIndex, CharSequence inContent)
482   {
483      mBuffer.insert(inIndex, inContent);
484
485      return this;
486   }
487
488   //--------------------------------------------------------------------------
489   public StringBuilderPlus insert(int inIndex, CharSequence inContent, int inStart, int inEnd)
490   {
491      mBuffer.insert(inIndex, inContent, inStart, inEnd);
492
493      return this;
494   }
495
496   //--------------------------------------------------------------------------
497   public StringBuilderPlus insert(int inIndex, boolean inContent)
498   {
499      mBuffer.insert(inIndex, inContent);
500
501      return this;
502   }
503
504   //--------------------------------------------------------------------------
505   public StringBuilderPlus insert(int inIndex, char inContent)
506   {
507      mBuffer.insert(inIndex, inContent);
508
509      return this;
510   }
511
512   //--------------------------------------------------------------------------
513   public StringBuilderPlus insert(int inIndex, int inContent)
514   {
515      mBuffer.insert(inIndex, inContent);
516
517      return this;
518   }
519
520   //--------------------------------------------------------------------------
521   public StringBuilderPlus insert(int inIndex, long inContent)
522   {
523      mBuffer.insert(inIndex, inContent);
524
525      return this;
526   }
527
528   //--------------------------------------------------------------------------
529   public StringBuilderPlus insert(int inIndex, float inContent)
530   {
531      mBuffer.insert(inIndex, inContent);
532
533      return this;
534   }
535
536   //--------------------------------------------------------------------------
537   public StringBuilderPlus insert(int inIndex, double inContent)
538   {
539      mBuffer.insert(inIndex, inContent);
540
541      return this;
542   }
543
544   //--------------------------------------------------------------------------
545   public int indexOf(String inString)
546   {
547      return mBuffer.indexOf(inString);
548   }
549
550   //--------------------------------------------------------------------------
551   public int indexOf(String inString, int inFromIndex)
552   {
553      return mBuffer.indexOf(inString, inFromIndex);
554   }
555
556   //--------------------------------------------------------------------------
557   public int lastIndexOf(String inString)
558   {
559      return mBuffer.lastIndexOf(inString);
560   }
561
562   //--------------------------------------------------------------------------
563   public int lastIndexOf(String inString, int inFromIndex)
564   {
565      return mBuffer.lastIndexOf(inString, inFromIndex);
566   }
567
568   //--------------------------------------------------------------------------
569   public StringBuilderPlus reverse()
570   {
571      mBuffer.reverse();
572
573      return this;
574   }
575
576   //--------------------------------------------------------------------------
577   @Override
578   public String toString()
579   {
580      return mBuffer.toString();
581   }
582
583   //--------------------------------------------------------------------------
584   public int length()
585   {
586      return mBuffer.length();
587   }
588
589   //--------------------------------------------------------------------------
590   public int capacity()
591   {
592      return mBuffer.capacity();
593   }
594
595   //--------------------------------------------------------------------------
596   public StringBuilderPlus ensureCapacity(int inMinimumCapacity)
597   {
598      mBuffer.ensureCapacity(inMinimumCapacity);
599
600      return this;
601   }
602
603   //--------------------------------------------------------------------------
604   public StringBuilderPlus trimToSize()
605   {
606      mBuffer.trimToSize();
607
608      return this;
609   }
610
611   //--------------------------------------------------------------------------
612   public StringBuilderPlus setLength(int inNewLength)
613   {
614      mBuffer.setLength(inNewLength);
615
616      return this;
617   }
618
619   //--------------------------------------------------------------------------
620   public char charAt(int inIndex)
621   {
622      return mBuffer.charAt(inIndex);
623   }
624
625   //--------------------------------------------------------------------------
626   public int codePointAt(int inIndex)
627   {
628      return mBuffer.codePointAt(inIndex);
629   }
630
631   //--------------------------------------------------------------------------
632   public int codePointBefore(int inIndex)
633   {
634      return mBuffer.codePointBefore(inIndex);
635   }
636
637   //--------------------------------------------------------------------------
638   public int codePointCount(int inBeginIndex, int inEndIndex)
639   {
640      return mBuffer.codePointCount(inBeginIndex, inEndIndex);
641   }
642
643   //--------------------------------------------------------------------------
644   public int offsetByCodePoints(int inIndex, int inCodePointOffset)
645   {
646      return mBuffer.offsetByCodePoints(inIndex, inCodePointOffset);
647   }
648
649   //--------------------------------------------------------------------------
650   public StringBuilderPlus getChars(int inSrcBegin, int inSrcEnd, char[] inDest, int inDestBegin)
651   {
652      mBuffer.getChars(inSrcBegin, inSrcEnd, inDest, inDestBegin);
653
654      return this;
655   }
656
657   //--------------------------------------------------------------------------
658   public StringBuilderPlus setCharAt(int inIndex, char inChar)
659   {
660      mBuffer.setCharAt(inIndex, inChar);
661
662      return this;
663   }
664
665   //--------------------------------------------------------------------------
666   public String substring(int inStart)
667   {
668      return mBuffer.substring(inStart);
669   }
670
671   //--------------------------------------------------------------------------
672   public String substring(int inStart, int inEnd)
673   {
674      return mBuffer.substring(inStart, inEnd);
675   }
676
677   //--------------------------------------------------------------------------
678   public CharSequence subSequence(int inStart, int inEnd)
679   {
680      return mBuffer.subSequence(inStart, inEnd);
681   }
682
683   //##########################################################################
684   // PRIVATE METHODS
685   //##########################################################################
686
687
688   //--------------------------------------------------------------------------
689   private void delimitIfNecessary()
690   {
691      if (length() > 0)
692      {
693         append(mDelimiter);
694      }
695   }
696
697   //--------------------------------------------------------------------------
698   private CharSequence processAddedContentForCarriageReturns(CharSequence inContent)
699   {
700      StringBuilderPlus buffer = new StringBuilderPlus(inContent);
701      int index = buffer.indexOf("\r", 1);
702      while (index > 0)
703      {
704         int lastLineFeedIndex = buffer.substring(0, index).lastIndexOf('\n');
705         buffer.replace(lastLineFeedIndex + 1, index + (lastLineFeedIndex < 0 ? 0 : 1), "");
706         index = buffer.indexOf("\r", lastLineFeedIndex);
707      }
708
709      return buffer;
710   }
711
712}