package util;
import java.util.ArrayList;
/**
* Ein StringBuffer, der aus einzelnen
* Teil-Strings besteht.
*
* Erst beim Umwandeln dieses
* {@link FragmentStringBuffer} in einen
* String mit der toString-Methode
* werden die Teil-Strings zusammengefügt.
*
* Das hat folgende Vorteile:
*
* Bei einem {@link java.lang.StringBuffer}
* wird die Kapazität bei jedem Grössen-Sprung
* verdoppelt, was schneller zu einem
* {@link OutOfMemoryError} führen kann.
*
* Dieser {@link FragmentStringBuffer}
* beansprucht nur den wirklich benötigten
* Speicherplatz (abgesehen von der internen
* Liste und der Verwaltung der Teil-Strings).
*
* Identische Teil-Strings können von
* der JVM als ein String gespeichert
* werden {@link java.lang.String#intern()}.
* (Doch nicht so implementiert, weil das
* zu einem PermGenSpace-Überlauf führen kann).
*
* Dadurch kann bis zum Zusammenbau der
* Teil-Strings weiterer Speicherplatz
* gespart werden.
*
* Nur beim Zusammenbau wird der
* doppelte Speicherplatz benötigt,
* was aber durch die Implementierung
* von {@link java.lang.String}
* nicht zu vermeiden ist.
*
* TODO equals, hashCode, clone und compareTo
*
* @author Heiner Kücker
*/
public class FragmentStringBuffer
{
/**
* Liste der Teil-Strings
*/
private ArrayList fragmentList = new ArrayList();
/**
* Gesamt-Länge aller Teil-Strings
*/
private int length;
/**
* Constructor.
*/
public FragmentStringBuffer()
{
super();
}
/**
* Constructor.
* @param pStr initialer String
*/
public FragmentStringBuffer(
final String pStr )
{
super();
append( pStr );
}
/**
* @return the length
*/
public int length()
{
return this.length;
}
/**
* Anhängen eines Strings.
* @param pStr anzuhängender String
*/
public void append(
final String pStr)
{
this.fragmentList.add(
// für die Zusammenfassung identischer Teil-Strings sorgen
pStr
// Nach dem Hinweis von Jochen Theodorou und Bernd Eckenfels soll man dies nicht machen
//.intern()
);
final long tNewLength = (long) this.length + pStr.length();
// es ist zu unwahrscheinlich, dass der StringBuffer jemals so riesig wird
// if ( tNewLength > Integer.MAX_VALUE )
// {
// throw new RuntimeException(
// "FragmentStringBuffer size "
// + tNewLength
// + " exceed max size: "
// + Integer.MAX_VALUE );
// }
this.length = (int) tNewLength;
}
/**
* @param pValue
*/
public void append(
final Object pValue)
{
append( String.valueOf( pValue ) );
}
/**
* @param pChar
*/
public void append(
final char pChar )
{
append( String.valueOf( pChar ) );
}
/**
* @see java.lang.Object#toString()
*/
public String toString()
{
// while ( Runtime.getRuntime().freeMemory() < this.length() * 2 )
// {
// System.out.println( "FragmentStringBuffer Memory full" );
// DebugUtil.printStacktraceToSysout();
// System.gc();
// Runtime.getRuntime().runFinalization();
// }
final char[] chrArr = new char[ this.length ];
int pos = 0;
for ( int i = 0 ; i < this.fragmentList.size() ; i++ )
{
final String partStr = (String) this.fragmentList.get( i );
for ( int j = 0 ; j < partStr.length() ; j++ )
{
chrArr[ pos++ ] = partStr.charAt( j );
}
}
return new String( chrArr );
}
/**
* @return Inhalt als kompletter String
*/
public String toStringAndClear()
{
// while ( Runtime.getRuntime().freeMemory() < this.length() * 2 )
// {
// System.out.println( "FragmentStringBuffer Memory full" );
// DebugUtil.printStacktraceToSysout();
// System.gc();
// Runtime.getRuntime().runFinalization();
// }
final char[] chrArr = new char[ this.length ];
int pos = 0;
for ( int i = 0 ; i < this.fragmentList.size() ; i++ )
{
final String partStr = (String) this.fragmentList.get( i );
// Löschen des Teil-Strings
this.fragmentList.set( i , null );
for ( int j = 0 ; j < partStr.length() ; j++ )
{
chrArr[ pos++ ] = partStr.charAt( j );
}
}
clear();
// Achtung: dies verhindert die Wiederverwendung dieses Buffers
this.fragmentList = null;
return new String( chrArr );
}
/**
* Leeren dieses {@link FragmentStringBuffer}
*/
public void clear()
{
this.fragmentList.clear();
this.length = 0;
}
/**
* @return empty or not
*/
public boolean isEmpty()
{
return this.length == 0;
}
/**
* @see java.lang.Object#finalize()
*/
// Nach dem Hinweis von Jochen Theodorou und Bernd Eckenfels soll man dies nicht machen
// protected void finalize() throws Throwable
// {
// // dem Garbage-Collector ein wenig helfen
// this.fragmentList.clear();
// super.finalize();
// }
/**
* Test.
* @param args
*/
// public static void main(String[] args)
// {
// final FragmentStringBuffer strBuff = new FragmentStringBuffer( "abc" );
// strBuff.append( "def" );
// strBuff.append( "ghi" );
// System.out.println( strBuff );
// }
}