Package net.helipilot50.stocktrade.framework

Source Code of net.helipilot50.stocktrade.framework.TextData

/*
Copyright (c) 2003-2009 ITerative Consulting Pty Ltd. All Rights Reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:

o Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
 
o Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
   
o This jcTOOL Helper Class software, whether in binary or source form may not be used within,
or to derive, any other product without the specific prior written permission of the copyright holder

 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


*/
package net.helipilot50.stocktrade.framework;

import java.awt.Component;
import java.beans.PropertyChangeEvent;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.text.CharacterIterator;
import java.text.Collator;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.StringCharacterIterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JLabel;

import net.helipilot50.stocktrade.displayproject.UIutils;

import org.apache.commons.beanutils.PropertyUtils;


/**
* TextData - a Forte framework class for scalar String value TextData is a data
* type class in the Forte net.helipilot50.stocktrade.framework.
*/
public class TextData extends DataValue implements Serializable, Comparable<Object> {
    // private static Logger logger = Logger.getLogger(TextData.class);
    /**
     *
     */
    private static final long serialVersionUID = 1071972331187067808L;
    /**
     * stores the reference to an Object whose property will be updated if the TextData's value is changed
     */
    @CloneReferenceOnly(nullReference=false)
    protected transient Object beanRef;
   
    /**
     * stores the property name to be updated if the TextData's value changes
     */
    @CloneReferenceOnly(nullReference=false)
    protected transient String beanProp;

    public static char cBACKSLASH_CHAR = '\\';

    /**
     * theString is the object which contains the actual string
     */
    // AD:25/11/2008: Found that allocating 64 caused performance issue in a performance test environment
    // Set back to use the default amount of 16.
    private StringBuilder theString = new StringBuilder();

    /**
     * theOffset stores the current position in string
     */
    protected int theOffset;


    /**
     * Resolves ambiguous constructor arguments
     *
     */
    public static class qq_Resolver {
        public static final int cBOOLEANVALUE = 1;
        public static final int cDOUBLEVALUE = 2;
        public static final int cINTEGERVALUE = 3;
        public static final int cISNULL = 4;
        public static final int cOFFSET = 5;
        public static final int cTEXTVALUE = 6;
        public static final int cVALUE = 7;
        public static final int cBOOLEANVALUE_ISNULL = 8;
        public static final int cBOOLEANVALUE_OFFSET = 9;
        public static final int cDOUBLEVALUE_ISNULL = 10;
        public static final int cDOUBLEVALUE_OFFSET = 11;
        public static final int cINTEGERVALUE_ISNULL = 12;
        public static final int cINTEGERVALUE_OFFSET = 13;
        public static final int cISNULL_TEXTVALUE = 14;
        public static final int cISNULL_OFFSET = 15;
        public static final int cISNULL_VALUE = 16;
        public static final int cOFFSET_TEXTVALUE = 17;
        public static final int cOFFSET_VALUE = 18;
        public static final int cBOOLEANVALUE_ISNULL_OFFSET = 19;
        public static final int cDOUBLEVALUE_ISNULL_OFFSET = 20;
        public static final int cINTEGERVALUE_ISNULL_OFFSET = 21;
        public static final int cISNULL_TEXTVALUE_OFFSET = 22;
        public static final int cISNULL_OFFSET_VALUE = 23;
        public static final int cINTEGERVALUE_TEXTVALUE = 24;
    }

//    public Object clone() {
//        return this.clone(false);
//    }
//
//    public Object clone(boolean pDeep) {
//        TextData result = new TextData();
//        this.duplicateIntoTarget(result, pDeep);
//        return result;
//    }

    public void duplicateIntoTarget(TextData pData, boolean pDeep) {
        super.duplicateIntoTarget(pData, pDeep);
        pData.theOffset = this.theOffset;
        // TF:7 oct. 2008:Changed to use the existing stringbuilder
        pData.theString.setLength(0);
        pData.theString.append(this.theString);
    }

    public TextData(TextData value) {
        // TF:8/11/07:Made null aware, and fixed up so it's not a clone method essentially!
      // TF:10/10/2008:Performance optimisation
        this();
        if (value == null) {
          // Do nothing, the value must already be empty
        } else if (value.hasNullValue) {
            setIsNull(true);
        } else {
            theString.append(value.theString);
        }
    }

    public TextData(int pSize){
      this();
      this.theString.ensureCapacity(pSize);
    }

    public TextData(boolean pValue, int pResolver) {
        super();
        if (pResolver == TextData.qq_Resolver.cBOOLEANVALUE)
            this.setValue(pValue);
        else if (pResolver == TextData.qq_Resolver.cISNULL) {
            this.setIsNull(pValue);
        }
    }

    public TextData(double pValue, int pResolver) {
        super();
        this.setValue(pValue);
    }

    public TextData(int pValue, int pResolver) {
        super();
        if (pResolver == TextData.qq_Resolver.cINTEGERVALUE)
            this.setValue(pValue);
        else if (pResolver == TextData.qq_Resolver.cOFFSET)
            this.setOffset(pValue);
        // TF:Added this to handle condition where  passed in resolver value is cDOUBLEVALUE.
        else if(pResolver == TextData.qq_Resolver.cDOUBLEVALUE)
            this.setValue((double)pValue);
    }

    public TextData(TextData pValue, int pResolver) {
        super();
        this.setValue(pValue);
    }

    public TextData(String pValue, int pResolver) {
        super();
        this.setValue(pValue);
    }

    public TextData(boolean pValue, boolean pValue2, int pResolver) {
        super();
        this.setValue(pValue);
    }

    public TextData(double pValue, boolean pValue2, int pResolver) {
        super();
        this.setValue(pValue);
    }

    public TextData(double pValue, int pValue2, int pResolver) {
        super();
        this.setValue(pValue);
        this.setOffset(pValue2);
    }

    public TextData(int pValue, boolean pValue2, int pResolver) {
        super();
        this.setValue(pValue);
    }

    public TextData(int pValue, int pValue2, int pResolver) {
        super();
        this.setValue(pValue);
        this.setOffset(pValue2);
    }

    public TextData(boolean pValue, int pValue2, int pResolver) {
        super();
        if (pResolver == TextData.qq_Resolver.cISNULL_OFFSET) {
            this.setOffset(pValue2);
        } else if (pResolver == TextData.qq_Resolver.cBOOLEANVALUE_OFFSET) {
            this.setValue(pValue);
            this.setOffset(pValue2);
        }
    }

    public TextData(boolean pValue, TextData pValue2, int pResolver) {
        super();
        this.setValue(pValue2);
    }

    public TextData(boolean pValue, String pValue2, int pResolver) {
        super();
        this.setValue(pValue2);
    }

    public TextData(TextData pValue, int pValue2, int pResolver) {
        super();
        this.setValue(pValue);
        this.setOffset(pValue2);
    }

    public TextData(int pValue, TextData pValue2, int pResolver) {
        super();
        this.setOffset(pValue);
        this.setValue(pValue2);
    }

    public TextData(int pValue, String pValue2, int pResolver) {
        super();
        if (pResolver == qq_Resolver.cOFFSET_VALUE){
          this.setValue(pValue2);
          this.setOffset(pValue);
        } else if (pResolver == qq_Resolver.cINTEGERVALUE_TEXTVALUE){ //PM:26/07/2008:
          this.setIntegerValue(pValue);
          this.setTextValue(pValue2);
        }
    }

    public TextData(boolean pValue, boolean pValue2, int pValue3, int pResolver) {
        super();
        this.setValue(pValue);
        this.setOffset(pValue3);
    }

    public TextData(double pValue, boolean pValue2, int pValue3, int pResolver) {
        super();
        this.setValue(pValue);
        this.setOffset(pValue3);
    }

    public TextData(int pValue, boolean pValue2, int pValue3, int pResolver) {
        super();
        this.setValue(pValue);
        this.setOffset(pValue3);
    }

    public TextData(boolean pValue2, TextData pValue, int pValue3, int pResolver) {
        super();
        this.setValue(pValue);
        this.setOffset(pValue3);
    }

    public TextData(boolean pValue2, String pValue, int pValue3, int pResolver) {
        super();
        this.setValue(pValue);
        this.setOffset(pValue3);
    }

    /**
     * default constructor - initializes TextData initializes an empty TextData
     * object
     */
    public TextData() {
      super();
    }

    /**
     * constructor - initializes TextData from String
     * <P>
     * <P>
     * initializes a TextData object from a String
     *
     * @param string
     *            the string used to initialize TextData object
     */
    public TextData(String string) {
        this();
       
        // CraigM:06/01/2009 - Don't set the string to "null" if it is null.
        if (string != null) {
          // TF:7 oct. 2008:Performance optimisation
          this.theString.append(string);
        }
    }

    /**
     * constructor - initializes TextData from StringBuilder
     * <P>
     * <P>
     * initializes a TextData object from a StringBuilder
     *
     * @param string
     *            StringBuilder used to initialize TextData object
     */
    public TextData(StringBuilder string) {
        if (string == null)
            data_RaiseNilOperandExcpt();
        // TF:7 oct. 2008:Changed to use the existing stringbuilder
        theString.append(string);
    }

    /**
     * constructor - initializes TextData from StringBuffer
     * <P>
     * <P>
     * initializes a TextData object from a StringBuffer
     *
     * @param string
     *            StringBuffer used to initialize TextData object
     */
    public TextData(StringBuffer string) {
      this();
        if (string == null) {
            data_RaiseNilOperandExcpt();
        }
        this.setValue(string);
    }

    /**
     * constructor - initializes TextData from char[]
     * <P>
     * <P>
     * initializes a TextData object from a char[]
     *
     * @param chars
     *            char[] used to initialize TextData object
     */
    public TextData(char[] chars) {
        this(new String(chars));
    }

    /**
     * CraigM:26/06/2008 - Used by reportChange.  Update the bound property.
     */
    private void reportChangeAction() {
        try {
          PropertyUtils.setSimpleProperty(this.beanRef, this.beanProp, this.toString());
          if (this.beanRef instanceof JLabel) {
            UIutils.labelWidth(((JLabel)this.beanRef));
          }
        } catch (IllegalAccessException e) {
          UsageException ue = new UsageException("Cannot synchronise TextData to property [" + this.beanProp + "]", e);
            ErrorMgr.addError(ue);
          throw ue;                                           
        } catch (InvocationTargetException e) {
          UsageException ue = new UsageException("Cannot synchronise TextData to property [" + this.beanProp + "]", e);
            ErrorMgr.addError(ue);
          throw ue;                                           
        } catch (NoSuchMethodException e) {
          UsageException ue = new UsageException("Cannot synchronise TextData to property [" + this.beanProp + "]", e);
            ErrorMgr.addError(ue);
          throw ue;                                           
        }
    }

    /**
     * Fires a change event to remain synchronized
     */
    protected void reportChange() {
      //PM:26/07/2008:Performance improvement
      super.reportChange();

      if ((this.beanRef != null)
          &&(this.beanProp != null)
          &&(this.beanProp.length() != 0)){

        // CraigM:26/06/2008 - If we are reporting a change to a visual component.  Do the change on the EDT
        if (this.beanRef instanceof Component) {
          UIutils.invokeOnGuiThread(new Runnable() {
            public void run() {
              // TF+CraigM:04/07/2008 - Disable the event manager as we don't want client to be notified of this change (this will lead to an infinite loop)
              EventManager.disableEventPosting();
              try {
                TextData.this.reportChangeAction();
              }
              finally {
                EventManager.enableEventPosting();
              }
            }
          });
        }
        else {
          this.reportChangeAction();
        }

          // -----------------------------------------------------------------------
          // TF:29/01/2010:JCT-640:Changed this to invoke a method on the superclass
          // -----------------------------------------------------------------------
      if (this.hasNullValue) {
        this.firePropertyChange("value", "", null);
      }
      else {
        this.firePropertyChange("value", null, this /*theString.toString()*/);//PM:27/07/2008:performance
      }
      }
    }

    public BooleanNullable isEqual(String pSource, boolean pIgnoreCase) {
      BooleanNullable result = new BooleanNullable();
      if (((pSource == null) || (pSource == "")) && this.isNull()) {
        result.setValue(true);
      }
      else if (this.isNull() || pSource == null) {
        result.setIsNull(true);
      } else {
        result.setIsNull(false);
        if (pIgnoreCase) {
          result.setValue(pSource.compareToIgnoreCase(this.theString.toString()) == 0);
        }
        else {
          result.setValue(pSource.compareTo(this.theString.toString()) == 0);
        }
      }
      return result;
    }

    public BooleanNullable isEqual(String pSource) {
        return this.isEqual(pSource, false);
    }

    public BooleanNullable isEqual(TextData pSource) {
        return this.isEqual(pSource.toString(), false);
    }

    public BooleanNullable isEqual(TextData pSource, boolean pIgnoreCase) {
        return this.isEqual(pSource.toString(), pIgnoreCase);
    }

    public BooleanNullable isGreaterThan(String pSource, boolean pIgnoreCase) {
        BooleanNullable result = new BooleanNullable();
        if (this.isNull() || pSource == null) {
            result.setIsNull(true);
        } else {
            result.setIsNull(false);
            if (pIgnoreCase) {
                result.setValue(pSource.compareToIgnoreCase(this.theString.toString()) < 0);
            }
            else {
                result.setValue(pSource.compareTo(this.theString.toString()) < 0);
            }
        }
        return result;
    }

    public String toString() {
      // TF:22/02/2009:toString and getValue differ in how they behave when called
      // with a null value. toString in Forte returned "N/A", getValue() returns "".
      // This change may have ramifications on the GUI.
      if (hasNullValue) {
        return NL_FMSG_NULLVALUE;
      }
      else {
        return getValue();
      }
    }

    public BooleanNullable isGreaterThan(String pSource) {
        return this.isGreaterThan(pSource, false);
    }

    public BooleanNullable isGreaterThan(TextData pSource) {
        return this.isGreaterThan(pSource.toString(), false);
    }

    public BooleanNullable isGreaterThan(TextData pSource, boolean pIgnoreCase) {
        return this.isGreaterThan(pSource.toString(), pIgnoreCase);
    }

    public BooleanNullable isLessThan(String pSource, boolean pIgnoreCase) {
        BooleanNullable result = new BooleanNullable(false);
        if (this.isNull() || pSource == null) {
            result.setIsNull(true);
        } else {
            result.setIsNull(false);

            if (pIgnoreCase)
                result.setValue(pSource.compareToIgnoreCase(this.theString.toString()) > 0);
            else
                result.setValue(pSource.compareTo(this.theString.toString()) > 0);
        }
        return result;
    }

    public BooleanNullable isLessThan(String pSource) {
        return this.isLessThan(pSource, false);
    }

    public BooleanNullable isLessThan(TextData pSource) {
        return this.isLessThan(pSource.toString(), false);
    }

    public BooleanNullable isLessThan(TextData pSource, boolean pIgnoreCase) {
        return this.isLessThan(pSource.toString(), pIgnoreCase);
    }

    public BooleanNullable isNotEqual(String pSource, boolean pIgnoreCase) {
        BooleanNullable result = new BooleanNullable();
        if (this.isNull() || pSource == null) {
            result.setIsNull(true);
        } else {
            result.setIsNull(false);
            if (pIgnoreCase)
                result.setValue(pSource.compareToIgnoreCase(this.theString.toString()) != 0);
            else
                result.setValue(pSource.compareTo(this.theString.toString()) != 0);
        }
        return result;
    }

    public BooleanNullable isNotEqual(String pSource) {
        return this.isNotEqual(pSource, false);
    }

    public BooleanNullable isNotEqual(TextData pSource) {
        return this.isNotEqual(pSource.toString(), false);
    }

    public BooleanNullable isNotEqual(TextData pSource, boolean pIgnoreCase) {
        return this.isNotEqual(pSource.toString(), pIgnoreCase);
    }

    /**
     * getActualSize
     * <p>
     * returns length of string
     *
     * @return value of derived int attribute ActualSize
     */
    public int getActualSize() {
        return theString.length();
    }
   
    /**
     * getLength
     * <p>
     * returns length of string. This was an undocumented method in Forte
     *
     * @return the length of the underlying string.
     */
    public int getLength() {
      return theString.length();
    }

    /**
     * getBooleanValue
     * <P>
     * <P>
     * returns value of string as boolean
     *
     * @return value of string as a boolean derived attribute
     */
    public boolean getBooleanValue() {
        if (checkNullValue()) {
            data_RaiseInvalidMethodExcpt();
        }
        // TF:10/10/2008:This method should not affect the offset, and it's faster to directly compare it
        return theString.length() == 4 &&
            (theString.charAt(0) == 't' || theString.charAt(0) == 'T') &&
            (theString.charAt(1) == 'r' || theString.charAt(1) == 'R') &&
            (theString.charAt(2) == 'u' || theString.charAt(2) == 'U') &&
            (theString.charAt(3) == 'e' || theString.charAt(3) == 'E');
    }

    /**
     * AsBoolean - return string as a boolean
     * <P>
     * <P>
     * Convert the next set of characters in the string to a boolean. The string
     * must contain TRUE or FALSE (case insensitive.
     *
     * @return value of string as a boolean
     */
    public boolean asBoolean() {
        String substring;

        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();

        substring = theString.substring(theOffset);
        return substring.equalsIgnoreCase("true");
    }

    /**
     * setValue - set string to boolean
     * <P>
     * <P>
     * sets value of string to boolean: i.e. "TRUE" or "FALSE".
     *
     * @param value -
     *            boolean attribute to set string to
     */
    public void setValue(boolean value) {
        if (value)
            setValue("true");
        else
            setValue("false");
    }

    /**
     * getDoubleValue - return the value of the string expressed as a double.
     * <P>
     * <P>
     * returns value of double attribute
     *
     * @return value of double attribute
     */
    public double getDoubleValue() {
        double ret = (double) 0.0;
        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();

        // TE: CR25169 - trim the spaces before parsing (27/11/2007)
        String theTrimmedString = theString.toString().trim();

        if (theTrimmedString.length() > 0) {
            try {
                // Added this to make the conversion of strings to double Locale aware...
                Double value = new Double(DecimalFormat.getInstance().parse(theTrimmedString).doubleValue());
                ret = value.doubleValue();
            } catch (NumberFormatException e) {
                data_RaiseInvalidConversionExcpt(this, data_Double);
            } catch (ParseException e) {
                data_RaiseInvalidConversionExcpt(this, data_Double);
            }
        }
        return ret;
    }

    /**
     * setValue - double
     * <P>
     * <P>
     * sets value of string to double
     *
     * @param value -
     *            set string to this value
     */
    public void setValue(double value) {
//      String string = Double.toString(value);
        // Use DecimalFormat to be locale aware...
        String string = DecimalFormat.getInstance().format(value);
        setValue(string);
    }

    /**
     * getIntegerValue - get value of string as integer
     * <P>
     * <P>
     * returns value of int attribute
     *
     * @return value of string as integer
     */
    public int getIntegerValue() {
        int ret = 0;
        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();
        // TF:6/7/07Changed this to ensure that if value is just spaces (not just empty) it works correctly and returns 0
        String strValue = theString.toString().trim();
        if (strValue.length() > 0) {
            try {
                if (strValue.equalsIgnoreCase(NL_FMSG_NULLVALUE))
                    ret = 0;
                else {
                    Integer value = new Integer(strValue);
                    ret = value.intValue();
                }
            } catch (NumberFormatException e) {
              // TF:1 oct. 2008:There are 2 possibilities here: An invalid format like "abc" and a number too
              // big like "3000000000". In the first case we need to throw an exception, but in the latter
              // case we need to round to MAX_INT or MIN_INT. Note that the exception raised does not help
              // us distinguish between these 2 cases.
              if (strValue.matches("^[+-]?\\d+$")) {
                if (strValue.charAt(0) == '-') {
                  return Integer.MIN_VALUE;
                }
                else {
                  return Integer.MAX_VALUE;
                }
              }
              else {
                data_RaiseInvalidConversionExcpt(this, data_Integer);
              }
            }
        }
        return ret;
    }

    /**
     * getValue - get value as string
     * <P>
     * <P>
     *
     * @return value as string
     */
    public String getValue() {
      // TF: 19/3/08: Allowed this to return null if null, for use in subclasses
      // TF:Aug 14, 2008:Fixed this up to return empty string ("") instead of N/A (as per test results)
      if (hasNullValue) {
        return "";
      }
      else {
        return this.theString.toString();
      }
    }

    /**
     * setValue - set string to this integer
     * <P>
     * <P>
     * set string to be this integer
     *
     * @param value -
     *            set string to this value
     */
    public void setValue(int value) {
        String string = Integer.toString(value);
        setValue(string);
    }

    /**
     * getOffset - what is the offset into the string
     * <P>
     * <P>
     * returns value of attribute theOffset. This represents the current offset
     * into the string.
     *
     * @return value of theOffset
     */
    public int getOffset() {
        return theOffset;
    }

    /**
     * AsCharPtr - access underlying StringBuilder
     * <P>
     * <P>
     * returns value of StringBuilder attribute
     *
     * @return StringBuilder - String attribute
     */
    public StringBuilder asCharPtr() {
        return theString;
    }

    /**
     * AsDataValue - access the string as a DataValue
     * <P>
     * <P>
     * Cast this to a DataValue and return
     *
     * @return DataValue - the string object as a DataValue
     */
    public DataValue asDataValue() {
        return (DataValue) this;
    }

    /**
     * Clear - clears the string completely of any characters
     * <P>
     * Clears strings and sets offset to zero
     */
    public void clear() {
        theOffset = 0;
        truncate();
    }
   
    public void setIsNull(boolean isNull) {
      // TF:30/07/2008:We should only need to clear the value when we were previously nullable
      // and as an optimisation, changed this to only set the null flag if it's different to it's old value
      if (hasNullValue != isNull) {
          if (isNull) {
            if (isNullable()) {
              this.setNull();
            }
            else {
              data_RaiseNullAssignExcpt();
            }
          } else {
              this.clearNull();
              this.clear();
          }
          reportChange();
      }
    }

    /**
     * Collate - check how the supplied String collates with this string
     * <P>
     * <P>
     * The return result is: 0 if both string collates equally > 0 if source
     * collates after this < 0 if source collates before this
     *
     * @return int - the result of the collation
     * @param source -
     *            the source string to compare with this string
     */
    public int collate(String source) {
        if (source == null)
            data_RaiseNilOperandExcpt();
        Collator myCollator = Collator.getInstance();
        return myCollator.compare(source, theString.toString());
    }

    /**
     * Collate - check how the supplied StringBuffer collates with this string
     * <P>
     * <P>
     * The return result is: 0 if both string collates equally > 0 if source
     * collates after this < 0 if source collates before this
     *
     * @return int - the result of the collation
     * @param source -
     *            the source string to compare with this string
     */
    public int collate(StringBuffer source) {
        return collate(source.toString());
    }

    /**
     * Collate - check how the supplied StringBuilder collates with this string
     * <P>
     * <P>
     * The return result is: 0 if both string collates equally > 0 if source
     * collates after this < 0 if source collates before this
     *
     * @return int - the result of the collation
     * @param source -
     *            the source string to compare with this string
     */
    public int collate(StringBuilder source) {
        return collate(source.toString());
    }

    /**
     * Collate - check how the supplied TextData collates with this string
     * <P>
     * <P>
     * The return result is: 0 if both string collates equally > 0 if source
     * collates after this < 0 if source collates before this
     *
     * @return int - the result of the collation
     * @param source -
     *            the source string to compare with this string
     */
    public int collate(TextData source) {
        return collate(source.theString);
    }

    /**
     * Compare - compare the given string with this string
     * <P>
     * <P>
     * This returns 0 if they are equal, < 0 if the string is less than source
     * and > 0 if the string is > source. If the ignorecase boolean is TRUE,
     * don't pay attention to case sensitivity.
     *
     * This functionality is used to build isGreaterThan etc.
     *
     * @return int - return value
     * @param ignorecase -
     *            boolean - true if case is to be ignored.
     * @param source -
     *            the string to be compared with this string
     */
    public int compare(String source, boolean ignorecase) {
        String sourceString;
        String thisString;

        if (source == null)
            data_RaiseNilOperandExcpt();
        if (!ignorecase) {
            sourceString = source;
            thisString = theString.toString();
        } else {
            sourceString = source.toLowerCase();
            thisString = theString.toString().toLowerCase();
        }
        return thisString.compareTo(sourceString);
    }

    /**
     * Compare - compare the given string with this string
     * <P>
     * <P>
     * This returns 0 if they are equal, < 0 if the string is less than source
     * and > 0 if the string is > source. If the ignorecase boolean is TRUE,
     * don't pay attention to case sensitivity.
     *
     * This functionality is used to build isGreaterThan etc.
     *
     * @return int - return value
     * @param ignorecase -
     *            boolean - true if case is to be ignored.
     * @param source -
     *            the string to be compared with this string
     */
    public int compare(TextData source, boolean ignorecase) {
        return this.compare(source.getValue(), ignorecase);
    }

    /**
     * Compare - compare the given string with this string
     * <P>
     * <P>
     * This returns 0 if they are equal, < 0 if the string is less than source
     * and > 0 if the string is > source. If the ignorecase boolean is TRUE,
     * don't pay attention to case sensitivity.
     *
     * This functionality is used to build isGreaterThan etc.
     *
     * @return int - return value
     * @param source -
     *            the string to be compared with this string
     */
    public int compare(String source) {
        return this.compare(source, false);
    }

    /**
     * Compare - compare the given string with this string
     * <P>
     * <P>
     * This returns 0 if they are equal, < 0 if the string is less than source
     * and > 0 if the string is > source. If the ignorecase boolean is TRUE,
     * don't pay attention to case sensitivity.
     *
     * This functionality is used to build isGreaterThan etc.
     *
     * @return int - return value
     * @param source -
     *            the string to be compared with this string
     */
    public int compare(TextData source) {
        return this.compare(source.getValue(), false);
    }

    /**
     * Compare - compare the given string with this string
     * <P>
     * <P>
     * This returns 0 if they are equal, < 0 if the string is less than source
     * and > 0 if the string is > source. If the ignorecase boolean is TRUE,
     * don't pay attention to case sensitivity.
     *
     * This functionality is used to build isGreaterThan etc.
     *
     * @return int - return value
     * @param ignorecase -
     *            boolean - true if case is to be ignored.
     * @param length -
     *            the length to compare
     * @param source -
     *            the string to be compared with this string
     */
    public int compare(int length, String source, boolean ignorecase) {
        String sourceString;
        String thisString;

        if (source == null)
            data_RaiseNilOperandExcpt();
        if (!ignorecase) {
            sourceString = source.substring(0, Math.min(length, source.length()));
            thisString = theString.substring(0, Math.min(length, theString.length()));
        } else {
            sourceString = source.toLowerCase().substring(0, Math.min(length, source.length()));
            thisString = theString.toString().toLowerCase()
                        .substring(0, Math.min(length, theString.length()));
        }
        return thisString.compareTo(sourceString);
    }

    /**
     * Compare - compare the given string with this string
     * <P>
     * <P>
     * This returns 0 if they are equal, < 0 if the string is less than source
     * and > 0 if the string is > source. If the ignorecase boolean is TRUE,
     * don't pay attention to case sensitivity.
     *
     * This functionality is used to build isGreaterThan etc.
     *
     * @return int - return value
     * @param length -
     *            the length to compare
     * @param ignorecase -
     *            boolean - true if case is to be ignored.
     * @param source -
     *            the string to be compared with this string
     */
    public int compare(int length, TextData source, boolean ignorecase) {
        return this.compare(length, source.getValue(), ignorecase);
    }

    /**
     * Compare - compare the given string with this string
     * <P>
     * <P>
     * This returns 0 if they are equal, < 0 if the string is less than source
     * and > 0 if the string is > source. If the ignorecase boolean is TRUE,
     * don't pay attention to case sensitivity.
     *
     * This functionality is used to build isGreaterThan etc.
     *
     * @return int - return value
     * @param length -
     *            the length to compare
     * @param source -
     *            the string to be compared with this string
     */
    public int compare(int length, String source) {
        return this.compare(length, source, false);
    }

    /**
     * Compare - compare the given string with this string
     * <P>
     * <P>
     * This returns 0 if they are equal, < 0 if the string is less than source
     * and > 0 if the string is > source. If the ignorecase boolean is TRUE,
     * don't pay attention to case sensitivity.
     *
     * This functionality is used to build isGreaterThan etc.
     *
     * @return int - return value
     * @param length -
     *            the length to compare
     * @param source -
     *            the string to be compared with this string
     */
    public int compare(int length, TextData source) {
        return this.compare(length, source.getValue(), false);
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate another string onto the end of this string. This can be used
     * to initialize a string, as well as add a new one to the end. If n is
     * zero, concatenate to the end of source, else concatenate only 'n' bytes.
     *
     * @return TextData - the new string
     * @param source -
     *            the parameter to be appended (as a string)
     * @param n -
     *            the maximum number of characters to be appended
     */
    public TextData concat(String source, int n) {
        if (source == null) {
            return this;
        }
        if (n < 0) {
            UsageException ex = new UsageException();
            ex.reasonCode = UsageException.PARAMETERERROR;
            throw ex;
        } else if (n > 0 && n < source.length()) { // CraigM:19/08/2008 - Added length check
            theString.append(source.substring(0, n));
        } else {
            theString.append(source);
        }
        this.clearNull();
        reportChange();
        return this;
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate another string onto the end of this string. This can be used
     * to initialize a string, as well as add a new one to the end. If n is
     * zero, concatenate to the end of source, else concatenate only 'n' bytes.
     *
     * @return TextData - the new string
     * @param source -
     *            the string to be appended (as a string)
     * @param n -
     *            the maximum number of characters to be appended
     */

    public TextData concat(StringBuffer source, int n) {
      // TF:7 oct. 2008:Optimised performance. Stopping this calling concat(source.toString())
      // prevents one array copy and one memory allocation
        if (source == null) {
            return this;
        }
        if (n < 0) {
            UsageException ex = new UsageException();
            ex.reasonCode = UsageException.PARAMETERERROR;
            ErrorMgr.addError(ex);
            throw ex;
        } else if (n > 0 && n < source.length()) {
            theString.append(source, 0, n);
        } else {
            theString.append(source);
        }
        this.clearNull();
        reportChange();
        return this;
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate another string onto the end of this string. This can be used
     * to initialize a string, as well as add a new one to the end. If n is
     * zero, concatenate to the end of source, else concatenate only 'n' bytes.
     *
     * @return TextData - the new string
     * @param source -
     *            the string to be appended (as a string)
     * @param n -
     *            the maximum number of characters to be appended
     */
    public TextData concat(StringBuilder source, int n) {
      // TF:7 oct. 2008:Optimised performance. Stopping this calling concat(source.toString())
      // prevents one array copy and one memory allocation
        if (source == null) {
            return this;
        }
        if (n < 0) {
            UsageException ex = new UsageException();
            ex.reasonCode = UsageException.PARAMETERERROR;
            ErrorMgr.addError(ex);
            throw ex;
        } else if (n > 0 && n < source.length()) {
            theString.append(source, 0, n);
        } else {
            theString.append(source);
        }
        this.clearNull();
        reportChange();
        return this;
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate another string onto the end of this string. This can be used
     * to initialize a string, as well as add a new one to the end. If n is
     * zero, concatenate to the end of source, else concatenate only 'n' bytes.
     *
     * @return TextData - the new string
     * @param source -
     *            the parameter to be appended (as a string)
     * @param n -
     *            the maximum number of characters to be appended
     */

    public TextData concat(TextData source, int n) {
        if (source == null) {
            return this;
        } else {
            return concat(source.theString, n);
        }
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate an integer onto the end of this string. This can be used to
     * initialize a string, as well as add a new one to the end. If n is zero,
     * concatenate to the end of source, else concatenate only 'n' bytes.
     *
     * @return TextData - the new string
     * @param source -
     *            the integer parameter to be appended (as a string)
     * @param n -
     *            the maximum number of characters to be appended
     */
    public TextData concat(int source, int n) {
        return concat(Integer.toString(source), n);
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate a double onto the end of this string. This can be used to
     * initialize a string, as well as add a new one to the end. If n is zero,
     * concatenate to the end of source, else concatenate only 'n' bytes.
     *
     * @return TextData - the new string
     * @param source -
     *            the double parameter to be appended (as a string)
     * @param n -
     *            the maximum number of characters to be appended
     */
    public TextData concat(double source, int n) {
      // AD:9/6/2008 Use DoubleData to conform to Forte standards
        return concat(new DoubleData(source).toString(), n);
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate "TRUE" or "FALSE" onto the end of this string. This can be
     * used to initialize a string, as well as add a new one to the end. If n is
     * zero, concatenate to the end of source, else concatenate only 'n' bytes.
     *
     * @return TextData - the new string
     * @param source -
     *            the boolean parameter to be appended (as a string)
     * @param n -
     *            the maximum number of characters to be appended
     */
    public TextData concat(boolean source, int n) {
        if (source) {
            return concat("true", n);
        } else {
            return concat("false", n);
        }
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate another string onto the end of this string. This can be used
     * to initialize a string, as well as add a new one to the end. If n is
     * zero, concatenate to the end of source, else concatenate only 'n' bytes.
     *
     * @return TextData - the new string
     * @param source -
     *            the parameter to be appended (as a string)
     * @param n -
     *            the maximum number of characters to be appended
     */
    public TextData concat(DataValue source, int n) {
        if (source == null) {
            return this;
        } else {
            return concat(source.toString(), n);
        }
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate another string onto the end of this string. This can be used
     * to initialize a string, as well as add a new one to the end.
     *
     * @return TextData - the new string
     * @param source -
     *            the parameter to be appended (as a string)
     */
    public TextData concat(Object source) {
        if (source == null) {
            return this;
        } else {
            return concat(source.toString(), 0);
        }
    }
    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate another string onto the end of this string. This can be used
     * to initialize a string, as well as add a new one to the end.
     *
     * @return TextData - the new string
     * @param source -
     *            the parameter to be appended (as a string)
     */
    public TextData concat(String source) {
        if (source == null) {
            return this;
        } else {
            return concat(source, 0);
        }
    }

    /**
     * Concat - append the passed parameter (as a char) to the end.
     * <P>
     * <P>
     * Concatenate another string onto the end of this string. This can be used
     * to initialize a string, as well as add a new one to the end.
     *
     * @return TextData - the new string
     * @param source -
     *            the parameter to be appended (as a char)
     */
    public TextData concat(char source) {
      this.theString.append(source);
        this.clearNull();
        reportChange();
        return this;
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate another string onto the end of this string. This can be used
     * to initialize a string, as well as add a new one to the end.
     *
     * @return TextData - the new string
     * @param source -
     *            the string to be appended (as a string)
     */

    public TextData concat(StringBuffer source) {
        if (source == null ) {
            return this;
        } else {
            return concat(source, 0);
        }
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate another string onto the end of this string. This can be used
     * to initialize a string, as well as add a new one to the end.
     *
     * @return TextData - the new string
     * @param source -
     *            the string to be appended (as a string)
     */

    public TextData concat(StringBuilder source) {
        if (source == null ) {
            return this;
        } else {
            return concat(source, 0);
        }
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate another string onto the end of this string. This can be used
     * to initialize a string, as well as add a new one to the end.
     *
     * @return TextData - the new string
     * @param source -
     *            the parameter to be appended (as a string)
     */

    public TextData concat(TextData source) {
        if (source == null) {
            return this;
        } else {
            return concat(source, 0);
        }
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate an integer onto the end of this string. This can be used to
     * initialize a string, as well as add a new one to the end.
     *
     * @return TextData - the new string
     * @param source -
     *            the integer parameter to be appended (as a string)
     */
    public TextData concat(int source) {
        return concat(source, 0);
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate a double onto the end of this string. This can be used to
     * initialize a string, as well as add a new one to the end.
     *
     * @return TextData - the new string
     * @param source -
     *            the double parameter to be appended (as a string)
     */
    public TextData concat(double source) {
        return concat(source, 0);
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate "TRUE" or "FALSE" onto the end of this string. This can be
     * used to initialize a string, as well as add a new one to the end.
     *
     * @return TextData - the new string
     * @param source -
     *            the boolean parameter to be appended (as a string)
     */
    public TextData concat(boolean source) {
        return concat(source, 0);
    }

    /**
     * Concat - append the passed parameter (as a string) to the end.
     * <P>
     * <P>
     * Concatenate another string onto the end of this string. This can be used
     * to initialize a string, as well as add a new one to the end.
     *
     * @return TextData - the new string
     * @param source -
     *            the parameter to be appended (as a string)
     */
    public TextData concat(DataValue source) {
        if (source == null) {
            return this;
        } else {
            return concat(source, 0);
        }
    }

    /**
     * CopyRange - return a substring of the this string
     * <P>
     * <P>
     * This will copy the selected text from the string. The definition of
     * selected text is the text from startposition to the end of the string.
     *
     * @return TextData - the new string
     * @param startoffset -
     *            start point of the new string
     */
    public TextData copyRange(int startoffset) {
        return copyRange(startoffset, getActualSize());
    }

    /**
     * CopyRange - return a substring of the this string
     * <P>
     * <P>
     * This will copy the selected text from the string. The definition of
     * selected text is the text from startposition to endposition. This method
     * will not alter the string in any way.
     *
     * @return TextData - the new string
     * @param endposition -
     *            end point of new string
     * @param startposition -
     *            start point of the new string
     */
    public TextData copyRange(int startposition, int endposition) {
        // TF:6/7/07:Discovered that in Forte, a negative second parameter made it the same as not passing it
        if (endposition < 0) {
            endposition = this.getActualSize();
        }
        TextData result;
        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();

        endposition = Math.min(getActualSize(), endposition);

        if (startposition > endposition) {
            UsageException ex = new UsageException("The startPosition parameter of the copyRange method is greater than endPosition or the text length");
            //ex.reasonCode UsageExceptionon.PARAMETERERROR;
            throw ex;
        }

        result = new TextData();

        // TF:7 oct. 2008:Optimised this. No need for reportChange() or result.clearNull() because
        // nothing else can currently have a reference to our textdata
        if (endposition == startposition) {
        } else if ((startposition == 0) && (endposition == getActualSize())) {
            result.theString.append(this.theString);
        } else {
          result.theString.append(this.theString, startposition, endposition);
        }
        return result;

    }
   
    /**
     * CutRange - cut a range of characters and return them as TextData
     * <P>
     * <P>
     * This will cut the selected text from the string. The definition of
     * selected text is the text from startposition to end of string. This
     * method will remove the selected text from the string as a side effect.
     *
     * @return TextData - the new String
     * @param startoffset -
     *            the offset to start cutting at
     */
    public TextData cutRange(int startoffset) {
        return cutRange(startoffset, getActualSize());
    }

    /**
     * CutRange - cut a range of characters and return them as TextData
     * <P>
     * <P>
     * This will cut the selected text from the string. The definition of
     * selected text is the text from startposition to endposition. This method
     * will remove the selected text from the string as a side effect.
     *
     * @return TextData - the new String
     * @param startposition -
     *            the offset to start cutting at
     * @param endposition -
     *            the offset to cut to
     */
    public TextData cutRange(int startposition, int endposition) {
      // TF:7 oct. 2008:Performance optimisation
        TextData result = new TextData();

        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();

        // CraigM 10/07/2007: Don't throw an exception if the start or end positions are invalid (as Forte doesn't).
        startposition = Math.max(0, startposition);
        endposition = Math.min(theString.length(), Math.max(0, endposition));

        if (startposition > endposition) {
            data_RaiseInvalidMethodExcpt();
        }

        if (endposition == startposition) {
          // Nothing to do, just return
            return result;
        }

        // Form a result string out of the removed characters
        try {
          result.theString.append(theString, startposition, endposition);
        } catch (IndexOutOfBoundsException e) {
            data_RaiseInvalidMethodExcpt();
        }

        // Remove the characters from the current string
        try {
          theString.delete(startposition, endposition);
        } catch (IndexOutOfBoundsException e) {
            data_RaiseInvalidMethodExcpt();
        }

        if (theOffset > startposition) {
            if (theOffset > endposition) {
                theOffset -= (endposition - startposition);
            }
            else {
                theOffset = startposition;
            }
        }
        reportChange();
        return (result);
    }

    /**
     * ExtBytes - extract 'bytes' (i.e. characters) from the string.
     * <P>
     * <P>
     * This will extract a number of 'bytes' (i.e. characters) from the string,
     * starting at the current character position. Reset the offset to the end
     * of the number of bytes copied. If 'n' is zero, copy to the end of the
     * string. IMPLEMENTATION Assume the caller wants n characters, not n bytes.
     *
     * @return int - number of characters extracted
     * @param target -
     *            StringBuffer to which characters will be appended
     * @param n -
     *            number of characters wanted
     */
    public int extBytes(StringBuffer target, int n) {

        String string = null;
        String origString = theString.toString();
        int end;
        int returned;

        if (target == null)
            data_RaiseNilOperandExcpt();

        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();

        if (n == 0) {
            end = origString.length();
        } else {
            end = theOffset + n;
            if (end > origString.length()) {
                end = origString.length();
            }
        }

        try {
            string = origString.substring(theOffset, end);
        } catch (IndexOutOfBoundsException e) {
            data_RaiseInvalidMethodExcpt();
        }
        target.append(string);

        returned = string.length();
        theOffset += returned;
        return (returned);
    }

    /**
     * ExtBytes - extract 'bytes' (i.e. characters) from the string.
     * <P>
     * <P>
     * This will extract a number of 'bytes' (i.e. characters) from the string,
     * starting at the current character position. Reset the offset to the end
     * of the number of bytes copied. This overload copies to the end of the
     * string. IMPLEMENTATION Assume the caller wants n characters, not n bytes.
     *
     * @return int - number of characters extracted
     * @param target -
     *            StringBuffer to which characters will be appended
     */
    public int extBytes(StringBuffer target) {
      return extBytes(target, 0);
    }

    /**
     * ExtBytes - extract 'bytes' (i.e. characters) from the string.
     * <P>
     * <P>
     * This will extract a number of 'bytes' (i.e. characters) from the string,
     * starting at the current character position. Reset the offset to the end
     * of the number of bytes copied. If 'n' is zero, copy to the end of the
     * string. IMPLEMENTATION Assume the caller wants n characters, not n bytes.
     *
     * @return int - number of characters extracted
     * @param target -
     *            StringBuilder to which characters will be appended
     * @param n -
     *            number of characters wanted
     */
    public int extBytes(StringBuilder target, int n) {

        String string = null;
        String origString = theString.toString();
        int end;
        int returned;

        if (target == null)
            data_RaiseNilOperandExcpt();

        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();

        if (n == 0) {
            end = origString.length();
        } else {
            end = theOffset + n;
            if (end > origString.length()) {
                end = origString.length();
            }
        }

        try {
            string = origString.substring(theOffset, end);
        } catch (IndexOutOfBoundsException e) {
            data_RaiseInvalidMethodExcpt();
        }
        target.append(string);

        returned = string.length();
        theOffset += returned;
        return (returned);
    }

    /**
     * ExtBytes - extract 'bytes' (i.e. characters) from the string.
     * <P>
     * <P>
     * This will extract a number of 'bytes' (i.e. characters) from the string,
     * starting at the current character position. Reset the offset to the end
     * of the number of bytes copied. This overload copies to the end of the
     * string. IMPLEMENTATION Assume the caller wants n characters, not n bytes.
     *
     * @return int - number of characters extracted
     * @param target -
     *            StringBuilder to which characters will be appended
     * @param n -
     *            number of characters wanted
     */
    public int extBytes(StringBuilder target) {
      return extBytes(target, 0);
    }
   
    /**
     * ExtBytes - extract 'bytes' (i.e. characters) from the string.
     * <P>
     * <P>
     * This will extract a number of 'bytes' (i.e. characters) from the string,
     * starting at the current character position. Reset the offset to the end
     * of the number of bytes copied. If 'n' is zero, copy to the end of the
     * string. IMPLEMENTATION Assume the caller wants n characters, not n bytes.
     *
     * @return int - number of characters extracted
     * @param target -
     *            StringBuilder to which characters will be appended
     * @param n -
     *            number of characters wanted
     */
    public int extBytes(char[] target, int n) {

        String string = null;
        String origString = theString.toString();
        int end;
        int returned;

        if (target == null)
            data_RaiseNilOperandExcpt();

        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();

        if (n == 0) {
            end = origString.length();
        } else {
            end = theOffset + n;
            if (end > origString.length()) {
                end = origString.length();
            }
        }

        try {
            string = origString.substring(theOffset, end);
        } catch (IndexOutOfBoundsException e) {
            data_RaiseInvalidMethodExcpt();
        }
        char[] src = string.toCharArray();
        for (int i = 0; i < src.length; i++) {
          target[i] = src[i];
        }

        returned = string.length();
        theOffset += returned;
        return (returned);
    }

    /**
     * ExtBytes - extract 'bytes' (i.e. characters) from the string.
     * <P>
     * <P>
     * This will extract a number of 'bytes' (i.e. characters) from the string,
     * starting at the current character position. Reset the offset to the end
     * of the number of bytes copied. This overload copies to the end of the
     * string. IMPLEMENTATION Assume the caller wants n characters, not n bytes.
     *
     * @return int - number of characters extracted
     * @param target -
     *            StringBuilder to which characters will be appended
     * @param n -
     *            number of characters wanted
     */
    public int extBytes(char[] target) {
      return extBytes(target, 0);
    }

    /**
     * FillString - fill the passed in TextData object with this string
     * <P>
     * <P>
     * Fill the passed in TextData object with this string
     *
     * @param target -
     *            the TextData object to be filled
     */
    public void fillString(TextData target) {
      // TF:7 oct. 2008:Performance optimisation
        if (target == null) {
            data_RaiseNilOperandExcpt();
        }
        // TF:14/10/2008:Testing shows that Forte doesn't replace the existing string, just
        // concatenates onto the end of it.
        //target.theString.setLength(0);
        // Don't use getFillString here as it's faster to append a stringBuffer onto a StringBuffer
        if (getIsNull()) {
          target.theString.append(NL_FMSG_NULLVALUE);
        }
        else {
          target.theString.append(this.theString);
        }
        // TF:29 Oct 2008:Corrected the value being altered to be the target
        target.theOffset = 0;
        target.hasNullValue = false;
        target.reportChange();
    }
   
    public String asString(){
        if (getIsNull()) {
            return NL_FMSG_NULLVALUE;
        }
        else {
            return this.theString.toString();
        }
    }
    /**
     * FindLine - find the beginning and end of a given line
     * <P>
     * <P>
     *
     * @return boolean - true if the line was found
     * @param Line -
     *            the line number to find (starting counting at 1)
     * @param startOffset -
     *            return the start of the line
     * @param endOffset -
     *            return the end of the line
     */
    public boolean findLine(int line, ParameterHolder startOffset, ParameterHolder endOffset) {
        int start = 0;
        int end = 0;
        int count = 1;
        boolean found = false;
        String string = theString.toString();
        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();

        if ((startOffset == null) || (endOffset == null)) {
            data_RaiseNilOperandExcpt();
        }

        for (; end != -1; count++) {
            end = string.indexOf('\n', start);
            if (count == line) {
                if (end == -1) {
                    end = string.length();
                }
                found = true;
                break;
            }
            start = end + 1;
        }

        if (found) {
            startOffset.setInt(start);
            endOffset.setInt(end);
        }
        return found;
    }

    /**
     * getAllocatedSize - returns the current memory allocation of the string
     * <P>
     * <P>
     * Here we'll return the current capacity of the StringBuilder expressed as
     * bytes (i.e. 2 for each character).
     *
     * @return int - capacity of string (in bytes)
     */
    public int getAllocatedSize() {
        return theString.capacity() * 2;
    }

    /**
     * getLineNumber - find the line number containing the offset specified
     * <P>
     * <P>
     * Given an offset within the String return the line number it lives within.
     * Return 0 if the Offset is invalid.
     *
     * @return int - the line number
     * @param Offset -
     *            the offset to find line number for
     */
    public int getLineNumber(int offset) {

        int count = 0;
        String string = theString.toString();
        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();

        if ((offset >= 0) && (offset < string.length())) {
            count = 1;
            for (int start = 0, end = 0;; count++) {
                end = string.indexOf('\n', start);
                if ((end == -1) || (end >= offset)) {
                    break;
                }
                start = end + 1;
            }
        }

        return count;
    }

    /**
     * IsAlpha - is the character at the current offset an alphabetic character?
     * <P>
     * <P>
     * Uses Character.isLetter()
     *
     * @return boolean - true or false
     */
    public boolean isAlpha() {
        char c;
        try {
            c = theString.charAt(theOffset);
        } catch (IndexOutOfBoundsException e) {
            return false;
        }
        return Character.isLetter(c);
    }

    /**
     * IsAtEnd - is the offset positioned at the end of the string?
     * <P>
     * <P>
     * If theOffset is at the end of the string, return true.
     *
     * @return boolean - true or false
     */
    public boolean isAtEnd() {
        return (theOffset == theString.length());
    }

    /**
     * IsChar - is the character at the current offset in the list provided?
     * <P>
     * <P>
     * Check to see if the current offset character is one of a list of
     * characters in the string. Return TRUE if it is, and FALSE if it is not.
     *
     * @return boolean - true or false
     * @param listofchars -
     *            the list of characters
     */
    public boolean isChar(String listofchars) {
        try {
            if (theOffset < 0 || this.theOffset >= this.theString.length()) {
                return false;
            }
            char c = theString.charAt(theOffset);
            return listofchars.indexOf((int) c) != -1;
        } catch (IndexOutOfBoundsException e) {
            return false;
        }
    }

    /**
     * IsChar - is the character at the current offset in the list provided?
     * <P>
     * <P>
     * Check to see if the current offset character is one of a list of
     * characters in the string. Return TRUE if it is, and FALSE if it is not.
     *
     * @return boolean - true or false
     * @param listofchars -
     *            the list of characters
     */
    public boolean isChar(TextData listofchars) {
        return isChar(listofchars.theString.toString());
    }

    /**
     * IsCntrl - is the character at the current offset a control character?
     * <P>
     * <P>
     * Uses Character.getType()
     *
     * @return boolean - true or false. If the index is out of range, <b>true</b>
     * will be returned to mimic Forte behaviour
     */
    public boolean isCntrl() {
        char c;
        try {
            c = theString.charAt(theOffset);
        } catch (IndexOutOfBoundsException e) {
            return true;
        }
        return (Character.getType(c) == Character.CONTROL);

    }

    /**
     * IsDigit - is the character at the current offset a digit?
     * <P>
     * <P>
     * Character.isDigit
     *
     * @return boolean - true or false
     */
    public boolean isDigit() {
        char c;
        try {
            c = theString.charAt(theOffset);
        } catch (IndexOutOfBoundsException e) {
            return false;
        }
        return Character.isDigit(c);
    }

    /**
     * IsGaiji - is the character at the current offset a Gaiji character?
     * <P>
     * <P>
     * checks if character is in the ranges for Gaiji characters as specified in
     * the unicode standard.
     *
     * @return boolean - true or false
     * @deprecated this method is not implemented
     */
    public boolean isGaiji() {
        return false;
    }

    /**
     * IsHangul - is the character at the current offset a Hangul character?
     * <P>
     * <P>
     * checks if character is in the ranges for Hangul characters as specified
     * in the unicode standard.
     *
     * @return boolean - true or false
     */
    public boolean isHangul() {
        boolean retStatus = false;
        try {
            char c = theString.charAt(theOffset);
            int value = 0x0000ffff & (int) c;
            if (value >= 0xac00 && value <= 0xd7a3)
                retStatus = true;
        } catch (IndexOutOfBoundsException e) {
        }
        return retStatus;
    }

    /**
     * IsHanja - is the character at the current offset a Hanja character?
     * <P>
     * <P>
     * checks if character is in the ranges for Hanja characters as specified in
     * the unicode standard.
     *
     * @return boolean - true or false
     */
    public boolean isHanja() {
        return this.isCJK();
    }

    /**
     * IsHanKana - is the character at the current offset a HanKana character?
     * <P>
     * <P>
     * checks if character is in the ranges for HanKana characters as specified
     * in the unicode standard.
     *
     * @return boolean - true or false
     */
    public boolean isHanKana() {
        char c;
        boolean retStatus = false;
        try {
            c = theString.charAt(theOffset);
            int value = 0x0000ffff & (int) c;
            if (value >= 0xff61 && value <= 0xff9f)
                retStatus = true;
        } catch (IndexOutOfBoundsException e) {
        }
        return retStatus;
    }

    /**
     * IsHanzi - is the character at the current offset a Hanzi character?
     * <P>
     * <P>
     * checks if character is in the ranges for Hanzi characters as specified in
     * the unicode standard.
     *
     * @return boolean - true or false
     */
    public boolean isHanzi() {
        return this.isCJK();
    }

    /**
     * IsHiragana - is the character at the current offset a Hiragana character?
     * <P>
     * <P>
     * checks if character is in the ranges for Hiragana characters as specified
     * in the unicode standard.
     *
     * @return boolean - true or false
     */
    public boolean isHiragana() {
        char c;
        boolean retStatus = false;
        try {
            c = theString.charAt(theOffset);
            int value = 0x0000ffff & (int) c;
            if (value >= 0x3041 && value <= 0x3094)
                retStatus = true;
        } catch (IndexOutOfBoundsException e) {
        }
        return retStatus;
    }

    /**
     * IsKanji - is the character at the current offset a Kanji character?
     * <P>
     * <P>
     * checks if character is in the ranges for Kanji characters as specified in
     * the unicode standard.
     *
     * @return boolean - true or false
     */
    public boolean isKanji() {
        return this.isCJK();
    }

    /**
     * IsKatakana - is the character at the current offset a Katakana character?
     * <P>
     * <P>
     * checks if character is in the ranges for Katakana characters as specified
     * in the unicode standard.
     *
     * @return boolean - true or false
     */
    public boolean isKatakana() {
        char c;
        boolean retStatus = false;
        try {
            c = theString.charAt(theOffset);
            int value = 0x0000ffff & (int) c;
            if (value >= 0x30a1 && value <= 0x30fe)
                retStatus = true;
        } catch (IndexOutOfBoundsException e) {
        }
        return retStatus;
    }

    /**
     * IsLower - is this a lower case character?
     * <P>
     * <P>
     * Use the Character.isLowerCase() to determine.
     *
     * @return boolean - true or false
     */
    public boolean isLower() {
        char c;
        try {
            c = theString.charAt(theOffset);
        } catch (IndexOutOfBoundsException e) {
            return false;
        }
        return Character.isLowerCase(c);
    }

    /**
     * IsLTR - is this a character which should be displayed left-to-right?
     * <P>
     * <P>
     * description of method
     *
     * @return boolean
     */
    public boolean isLTR() {
        boolean retStatus = true;
        try {
            char c = theString.charAt(theOffset);
            int value = 0x0000ffff & (int) c;
            if (value >= 0x591 && value <= 0x6f9)
                retStatus = false;
        } catch (IndexOutOfBoundsException e) {
        }
        return retStatus;
    }

    /**
     * IsMbyte
     * <P>
     * <P>
     * Returns TRUE if the next character is a multibyte character.
     * As Java strings are Unicode (2 bytes) this method always returns true.
     */
    public boolean isMbyte() {
        return true;
    }

    /**
     * IsPunct
     * <P>
     * <P>
     * description of method
     *
     * @return boolean
     */
    public boolean isPunct() {
        char c;
        boolean retStatus = false;
        try {
            c = theString.charAt(theOffset);
            int value = 0x0000ffff & (int) c;
            if ((value >= 0x0021 && value <= 0x002f)
                    || (value >= 0x003a && value <= 0x0040)
                    || (value >= 0x005b && value <= 0x0060)
                    || (value >= 0x007b && value <= 0x007e)
                    || (value >= 0x00a1 && value <= 0x00bf)
                    || (value == 0x00d7 || value == 0x00f7)
                    || (value >= 0x2010 && value <= 0x2046))
                retStatus = true;
        } catch (IndexOutOfBoundsException e) {
        }
        return retStatus;
    }

    /**
     * IsRTL
     * <P>
     * <P>
     * description of method
     *
     * @return boolean
     */
    public boolean isRTL() {
        return !this.isLTR();
    }

    /**
     * IsSpace - is the current character a space character?
     * <P>
     * <P>
     * Uses Character.isWhitespace() to determine
     *
     * @return boolean - true or false
     */
    public boolean isSpace() {
        char c;
        try {
            c = theString.charAt(theOffset);
        } catch (IndexOutOfBoundsException e) {
            return false;
        }
        // TF:15/08/2008:Changed this to be isWhitespace, otherwise \r is not considered a space character
        return Character.isWhitespace(c);

    }

    /**
     * IsUpper - is this an upper case character?
     * <P>
     * <P>
     * Use the Character.isUpperCase() to determine.
     *
     * @return boolean - true or false
     */
    public boolean isUpper() {
        char c;
        try {
            c = theString.charAt(theOffset);
        } catch (IndexOutOfBoundsException e) {
            return false;
        }
        return Character.isUpperCase(c);
    }

    /**
     * LengthToEnd - how far is it from the offset to the end of the string?
     * <P>
     * <P>
     * return size minus offset
     *
     * @return int - how many characters between the offset and the end.
     */
    public int lengthToEnd() {
        return (theString.length() - theOffset);
    }

    /**
     * MoveNext - move the current position up by one character
     * <P>
     * bump theOffset by one unless already at the end.
     */
    public void moveNext() {
        if (theOffset < theString.length()) {
            theOffset++;
        }
    }

    /**
     * MovePrevious - move the current position back by one character
     * <P>
     * decrement the offset unless already at 0.
     */
    public void movePrevious() {
        if (theOffset > 0) {
            theOffset--;
        }
    }

    /**
     * CheckForNull - private routine to implement EOS matching
     * <P>
     * <P>
     * str Start of string representing the list of characters. The special
     * characters must be at the start of this string.
     *
     * output This StringBuilder
     * which must be provided by the caller is filled with the list of
     * characters, either the same as str if no characters representing the null
     * terminator were found at the start of the string, or offset to be beyond
     * those characters if they were. The StringBuilder should be empty
     *
     * returns
     * TRUE if the character was found, and FALSE if it was not found.
     *
     * @return boolean - true or false - should EOS be matched?
     * @param str -
     *            list of characters (as String) to be matched
     * @param output -
     *            modified list of characters to be matched as StringBuilder
     */
    private boolean checkForNull(String str, StringBuilder output) {
        boolean ret = false;
        if ((str == null) || (str.length() < 1)) return true;
        /**
         * OLD CODE
         * if ((str.charAt(0) == cBACKSLASH_CHAR) && (str.charAt(1) == '0')) {
         */
        /**
         * NEW CODE  CraigM: 8/8/05
         */
        if ((str.charAt(0) == cBACKSLASH_CHAR) && (str.length() > 1) && (str.charAt(1) == '0')) {
            ret = true;
            output.append(str.substring(2));
        } else {
            output.append(str);
        }
        return (ret);
    }

    private boolean isCJK() {
        char c;
        boolean retStatus = false;
        try {
            c = theString.charAt(theOffset);
            int value = 0x0000ffff & (int) c;
            if (value >= 0x4e00 && value <= 0x9fa5)
                retStatus = true;
        } catch (IndexOutOfBoundsException e) {
        }
        return retStatus;
    }

    /**
     * MoveToChar
     * <P>
     * <P>
     * Move the current offset until any of the characters in the null
     * terminated list is found. IMPLEMENTATION Looks for the special string
     * using CheckForNull
     *
     * @return boolean
     * @param listofchars -
     *            list of characters to match
     */
    public boolean moveToChar(String listofchars) {
        StringBuilder tmpMatch = new StringBuilder();
        String string = theString.toString();
        String charList;
        StringCharacterIterator iter;
        boolean matchEos;
        boolean ret;
        int numBytes;

        if (listofchars == null)
            data_RaiseNilOperandExcpt();

        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();

        // TF:17/07/2009:Added in check for empty list
        if (listofchars.length() == 0) {
          return false;
        }
       
        matchEos = checkForNull(listofchars, tmpMatch);
        charList = tmpMatch.toString();
        iter = new StringCharacterIterator(string.substring(theOffset));

        for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
            if (charList.indexOf(c) != -1) {
                break;
            }
        }
        numBytes = iter.getIndex();
        if ((string.length() == (theOffset + numBytes)) && !matchEos) {
            ret = false;
        } else {
            ret = true;
            theOffset += numBytes;
        }
        return ret;
    }

    /**
     * MoveToChar
     * <P>
     * <P>
     * Move the current offset until any of the characters in the null
     * terminated list is found. IMPLEMENTATION Looks for the special string
     * using CheckForNull
     *
     * @return boolean
     * @param listofchars -
     *            list of characters to match
     */
    public boolean moveToChar(TextData listofchars) {
        return moveToChar(listofchars.theString.toString());
    }

    /**
     * MoveToLastChar
     * <P>
     * <P>
     * Move the current offset until any of the characters in the null
     * terminated list is found. This starts the search at the end of the
     * string, and moves backwards. This also will not move the offset to a
     * point lower than where it currently is. IMPLEMENTATION The null
     * terminator in listofchars is not part of the search, unless the
     * CheckForNull comes up TRUE. In that case, it will use the null character
     * only if none of the others are found in the string.
     *
     * @return boolean - true if one of characters is found
     * @param listofchars -
     *            list of characters (String) to search for
     */
    public boolean moveToLastChar(String listofchars) {
        StringBuilder tmpMatch = new StringBuilder();
        String string = theString.toString();
        String charList;
        StringCharacterIterator iter;
        boolean matchEos;

        if (listofchars == null)
            data_RaiseNilOperandExcpt();

        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();

        // TF:17/07/2009:fixed this up
        if (listofchars.length() == 0) {
            return false;
        }

        matchEos = checkForNull(listofchars, tmpMatch);
        charList = tmpMatch.toString();
        iter = new StringCharacterIterator(string);

        for (char c = iter.last(); c != CharacterIterator.DONE; c = iter.previous()) {
            if (charList.indexOf(c) != -1) {
                theOffset = iter.getIndex();
                return true;
            }
        }
        if (matchEos) {
            theOffset = string.length();
            return (true);
        }
        return (false);
    }

    public boolean moveToLastChar(TextData listofchars) {
        return moveToLastChar(listofchars.theString.toString());
    }

    public boolean moveToNotChar(String listofchars) {
        StringBuilder tmpMatch = new StringBuilder();
        String string = theString.toString();
        String charList;
        StringCharacterIterator iter;
        boolean matchEos;
        boolean ret;
        int numBytes;

        if (listofchars == null)
            data_RaiseNilOperandExcpt();

        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();

        // TF:17/07/2009:Added check for length
        if (listofchars.length() == 0) {
          return true;
        }
        matchEos = checkForNull(listofchars, tmpMatch);
        charList = tmpMatch.toString();

        iter = new StringCharacterIterator(string.substring(theOffset));
        for (char c = iter.first(); c != CharacterIterator.DONE; c = iter .next()) {
            if (charList.indexOf(c) == -1) {
                break;
            }
        }
        numBytes = iter.getIndex();
        if ((string.length() == (theOffset + numBytes)) && !matchEos) {
            ret = false;
        } else {
            ret = true;
            theOffset += numBytes;
        }
        return ret;
    }

    public boolean moveToNotChar(TextData listofchars) {
        return moveToNotChar(listofchars.theString.toString());
    }

    public boolean moveToString(String source, boolean gopast, boolean ignorecase) {
        String searchString;
        String origString;
        int offset;

        if (source == null)
            data_RaiseNilOperandExcpt();

        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();

        if (ignorecase) {
            origString = theString.toString().substring(theOffset).toLowerCase();
            searchString = source.toLowerCase();
        } else {
            origString = theString.toString().substring(theOffset);
            searchString = source;
        }

        offset = origString.indexOf(searchString);

        if (offset == -1)
            return (false);
        theOffset += offset;
        if (gopast)
            theOffset += searchString.length();

        return (true);
    }

    public boolean moveToString(TextData source, boolean gopast,
            boolean ignorecase) {
        return moveToString(source.theString.toString(), gopast, ignorecase);
    }

    public boolean moveToString(String source, boolean gopast) {
        return moveToString(source, gopast, false);
    }

    public boolean moveToString(TextData source, boolean gopast) {
        return moveToString(source, gopast, false);
    }

    public boolean moveToString(String source) {
        return moveToString(source, false, false);
    }

    public boolean moveToString(TextData source) {
        return moveToString(source, false, false);
    }

    /**
     * The ReplaceParameters method replaces specified characters with specified strings.
     */
//  PM:12/12/07 now uses variable arguments
    public TextData replaceParameters(TextData source, Object ... parameters) {
        return replaceParameters(source.toString(), parameters);
    }
    /**
     * The ReplaceParameters method replaces specified characters with specified strings.
     * @param source
     * @param parameters
     * @return
     */
    //PM:12/12/07 now uses variable arguments
    //pM:28/04/08 changed parameter to Object, this allows anything to be passed and handles TextData/String
    public TextData replaceParameters(String source, Object ... parameters) {
        //this.theString.setLength(0);
        StringBuffer buffer = new StringBuffer();
        Pattern p = Pattern.compile("%(\\d)");
        Matcher m = p.matcher(source);
        while (m.find()){
            int i = m.group().charAt(1)-(int)'1'; //convert the ordinal value to array index
          // AD:3/6/2008 Escape backslash and dollar because the appendReplacement treats them specially
            // FIX:TF:29 Sep 2009:Check to see if the parameter index is in range
            String s;
            if (i >= parameters.length || i < 0 || parameters[i] == null) {
              s = "<NIL>";
            }
            else {
              s = parameters[i].toString().replace("\\", "\\\\").replace("$", "\\$");
            }
            m.appendReplacement(buffer, s);
        }
        m.appendTail(buffer);
//      System.out.println(this.theString);
        setValue(buffer);
        return this;
    }

    /**
     * The ReplaceRange method removes a portion of the TextData object on which you are invoking the method and replaces it with the string in the source parameter.
     * @param source
     * @param startoffset
     * @return
     */
    public TextData replaceRange(String source, int startoffset) {
        return replaceRange(source, startoffset, theString.length());
    }
    /**
     * The ReplaceRange method removes a portion of the TextData object on which you are invoking the method and replaces it with the string in the source parameter.
     * @param source
     * @param startoffset
     * @return
     */
    public TextData replaceRange(TextData source, int startoffset) {
        return replaceRange(source, startoffset, theString.length());
    }
    /**
     * The ReplaceRange method removes a portion of the TextData object on which you are invoking the method and replaces it with the string in the source parameter.
     * @param source
     * @param startoffset
     * @param endoffset
     * @return
     */
    public TextData replaceRange(String source, int startoffset, int endoffset) {
        if (source == null)
            data_RaiseNilOperandExcpt();

        if (checkNullValue())
            data_RaiseInvalidMethodExcpt();

        // CraigM 11/07/2007: Ingore invalid offsets.  This is what Forte does.
        startoffset = Math.max(0, startoffset);
        endoffset = Math.max(startoffset, endoffset);
        //PM:28/2/08 if end offset is longer than length, make it equal to length
        endoffset = Math.min(theString.length(), endoffset);

        // TF:7 oct. 2008:Removed redundant code and replaced it with a single line on the string builder
//        beforeCut = theString.substring(0, startoffset);
//        afterCut = theString.substring(endoffset);
//        theString = new StringBuilder(beforeCut + source + afterCut);

        theString.replace(startoffset, endoffset, source);

        if ((theOffset >= startoffset) && (theOffset < endoffset)) {
            theOffset = startoffset;
        }
        // TF:12/11/07: Despite the Forte documentation, if the offset is after the
        // insert point, the offset is incremented by the amount of the insert
        if (theOffset >= endoffset) {
            theOffset += source.length();
        }
        if (theOffset > theString.length()) {
            theOffset = theString.length();
        }
        reportChange();
        return this;

    }
    /**
     * The ReplaceRange method removes a portion of the TextData object on which you are invoking the method and replaces it with the string in the source parameter.
     * @param source
     * @param startoffset
     * @param endoffset
     * @return
     */
    public TextData replaceRange(TextData source, int startoffset, int endoffset) {
        return replaceRange(source.theString.toString(), startoffset, endoffset);
    }

    /**
     * The setAllocatedSize method changes the memory allocation for a TextData object.
     * @param n - Use the n parameter to allocate maximum size in bytes for the current
     * TextData object. If you set n to zero, this method frees up any extra memory
     * allocated to store the current text value
     * @param save - Use the save parameter to save (true) or clear (false) the current
     * TextData value; the default is <tt>false</tt>
     */
    public void setAllocatedSize(int n, boolean save) {
      // TF:7 oct. 2008:Performance optimisation
      if (n >= theString.capacity()) {
        theString.ensureCapacity(n);
      }
      else {
         theString.setLength(n);
           theString.trimToSize();
        if (!save) {
          theString.setLength(0);
        }
      }
        if (theOffset > theString.length()) {
            theOffset = theString.length();
        }
    }
    /**
     * Allocates object memory for a TextData object.
     * @param n
     */
    public void setAllocatedSize(int n) {
        setAllocatedSize(n, false);
    }

    /**
   * The Offset is the current byte offset within the TextData object. This
   * must be in the range from 0 (for empty TextValues) to length + 1 (just
   * beyond the current value of the TextData). The "Move" methods implicitly
   * change offset. If you set offset out of range it is ignored. The offset
   * will always be set to the first byte of a multi-byte character.@param
   * value
   */
    public void setOffset(int value) {
      // AD:3/6/2008 Recoded to ignore offsets out of range rather than reseting to 0
        if (value <= theString.length() && value >= 0) {
            theOffset = value;
        }
        // Ignore all others
    }

    public void setValue(char[] value) {
      if (value == null) {
        this.setValue((String)null);
      }
      else {
        this.setValue(new String(value));
      }
    }
    public void setValue(byte[] value) {
      if (value == null) {
        this.setValue((String)null);
      }
      else {
        this.setValue(new String(value));
      }
    }
   
    public void setValue(String value) {

        if (value == null) {
          // AGD:13/3/08: Commented out the below code because it was causing a null object exception
          // Also, it is not how Forte behaves. Forte converts the null to empty string
          // See JCT-502 for more details
            //clear();
            // TF:6/7/07:Changed to allow nulls to be set. This ensures it works properly in subclasses that are Nullable,
            // provided the superclass checks for nullability prior to setting the null value
            //setNull();
            value = "";
    }
        // TF:27/10/2008:If we're setting the value to the same value that we currently have, do nothing. This prevents
        // some GUI bugs like underlying data values changing when shift-tabbing through an array that has grid fields
        // inside them and textDatas mapped to data fields within the grid field.
        theOffset = 0;
        if (this.theString.length() == value.length() && hasNullValue == false && this.theString.toString().equals(value)) {
          return;
        }
        // TF:7 oct. 2008:Performance optimisation
        this.theString.setLength(0);
        this.theString.append(value);
        hasNullValue = false;
        reportChange();
    }

    public void setValue(StringBuilder value) {
        if (value == null) {
            // TF:6/7/07:Changed to allow nulls to be set. This ensures it works properly in subclasses that are Nullable,
            // provided the superclass checks for nullability prior to setting the null value
            setNull();
            clear();
        }
        // TF:28/07/2009:Changed this to test the strings for equality, not just the object references which should be equal
        // only in the case of identity equals. This stops a StackOverflowException in some mapped droplists.
        else if (value != theString && !theString.toString().equals(value.toString())) {
            theString.setLength(0);
            theString.append(value);
            theOffset = 0;
            hasNullValue = false;
            reportChange();
        }
    }

    public void setValue(StringBuffer value) {
      String newValue;
        if (value == null) {
            newValue = "";
        } else {
          newValue = value.toString();
        }
        setValue(newValue);
    }

    public void setValue(TextData source) {
        if (source == null) {
          // AGD:13/3/08: Commented out the below code because it was causing a null object exception
          // Also, it is not how Forte behaves. Forte converts the null to empty string
          // See JCT-502 for more details
            //clear();
            //setNull();
            setValue("");
        } else if (source.hasNullValue) {
            setIsNull(true);
        } else {
          // TF:25/08/2009:Changed this to call setValue(String) overload, as this is what Forte did.
            setValue(source.theString.toString());
        }
    }
   
    public void setValue(BinaryData source) {
      if (source == null) {
        setValue("");
      }
      else if (source.hasNullValue) {
        setIsNull(true);
      }
      else {
        setValue(new String(source.toByteArray()));
      }
    }

//    public void setValue(DataValue source) {
//        setValue(source.toString());
//    }
    public String getStringValue(){
      return toString();
    }
    public int sizeNext() {
        if (this.theOffset == theString.length())
            return 0;
        else
            return 2;
    }

    public int sizePrevious() {
        if (this.theOffset == 0)
            return 0;
        else
            return 2;
    }

    public TextData toLower() {
        String newString = theString.toString().toLowerCase();
        theString.setLength(0);
        theString.append(newString);
        reportChange();
        return this;
    }

    public TextData toUpper() {
        String newString = theString.toString().toUpperCase();
        theString.setLength(0);
        theString.append(newString);
        reportChange();
        return this;
    }

    /**
     * The trimLeading method trims all leading white space from the beginning of the
     * TextData object on which you are invoking the method forward to the first
     * non-white-space character.
     * <p>
     * The white space characters are those returned from Character.isWhitespace().
     * TrimLeading does not use the current offset to limit the conversion. It changes
     * the current offset only if it was in the white space it trims off. In that case
     * the method sets the current offset to the beginning of the text.
     * @return the current TextData
     */
    public TextData trimLeading() {
      // TF:7 oct. 2008:Performance optimisation
        if (checkNullValue()) {
            data_RaiseInvalidMethodExcpt();
        }
      int length = theString.length();
      int index;
      for (index = 0; index < length; index++) {
        if (!Character.isWhitespace(theString.charAt(index))) {
          break;
        }
      }
      if (index == 0) {
        return this;
      }
      if (index < length) {
        // There are index leading whitespaces
        this.theString.delete(0, index);
        this.theOffset -= index;
        this.theOffset = Math.max(0, this.theOffset);
      }
      else {
        // The whole thing is whitespace, clear it.
        this.theString.setLength(0);
        this.theOffset = 0;
      }
        reportChange();
        return this;
    }

    /**
     * The TrimTrailing method trims all trailing white space from the end of the
     * TextData back to the last non white space character.
     * <p>
     * The white space characters are those returned from Character.isWhitespace().
     * TrimTrailing does not use the current offset to limit the conversion. It
     * changes the current offset only if it was in the white space it trims off.
     * In that case the method sets the current offset at the end of the text.
     * @return the current TextData
     */
    public TextData trimTrailing() {
      // TF:7 oct. 2008:Performance optimisation, and re-instantional of null check
      // after validating against Forte
      if (checkNullValue()) {
        data_RaiseInvalidMethodExcpt();
      }
      int index;
      for (index = theString.length()-1; index >= 0; index--) {
        if (!Character.isWhitespace(theString.charAt(index))) {
          break;
        }
      }
      // AD:Nov 13, 2008 - minus 1 from length otherwise changes offset and never returns directly when there is no whitespace
      if (index == theString.length()-1) {
        // Nothing to do, just return
        return this;
      }
      if (index >= 0) {
        // There are trailing whitespaces
        this.theString.delete(index+1, theString.length());
          // AD:Nov 13, 2008 - Only change the offset if it is larger than the length
        if (this.theOffset > index+1) {
          this.theOffset = index+1;
        }
      }
      else {
        // The whole thing is whitespace, clear it.
        this.theString.setLength(0);
        this.theOffset = 0;
      }
        reportChange();
        return this;
    }

    /**
     * The <tt>truncate</tt> method truncates the TextData object on which you are
     * invoking the method, from the current offset to the end of the text.
     * <p>
     * The current offset does not change and ends up one past the end of the string.
     * @return
     */
    public TextData truncate() {
        theString.setLength(theOffset);
        hasNullValue = false;
        reportChange();
        return this;
    }

    public TextData wrapText() {
        return this.wrapText("", 50, 4);
    }

    public TextData wrapText(String prefix) {
        return this.wrapText(prefix, 50, 4);
    }

    public TextData wrapText(int LineWidth, int TabWidth) {
        return this.wrapText("", LineWidth, TabWidth);
    }

    /**
     * Wrap a textdata to LineWidth without ignoring line breaks, i.e. '\n'.
     * String.split() ignores lines breaks already within the textdata.
     *
     * @param LineWidth
     * @return
     */
    public TextData wrapText(int LineWidth) {

        TextData result = new TextData();
        final String tab = "    "
        int minWidth = 50;
        int charCount = 0;    // the number of chars on current line
        int spaceIndex = 0;    // index in result of last space
        char c;          // current char

        //------------------------------
        // make minWidth at least 50
        //------------------------------
        if(LineWidth > minWidth) minWidth = LineWidth;

        for(int i = 0 ; i < this.length(); i++){

            c = this.charAt(i);         

            //-----------------------------------------------
            // reset the char count if we have a line break
            //-----------------------------------------------
            if(c == '\n') {
                result.append(c);
                charCount = 0;
                spaceIndex = 0;
                continue;
            }
            else if(c == '\t'){
                result.append(tab);
                charCount = charCount + tab.length();
            }
            else if(c == ' '){
                //--------------------------------------------------------------------
                // if prev char was line break, do not add space at start of new line
                //--------------------------------------------------------------------
                if(this.charAt(i - 1) == '\n'){
                    continue;
                }
                //------------------------------------------------------------------
                // if the next char is a line break, do not check width as below
                //------------------------------------------------------------------
                else if(this.charAt(i + 1) == '\n'){
                    result.append(c);
                    continue;
                }
                //---------------------------------------------
                // record index of last space in result string
                //---------------------------------------------
                else{
                    result.append(c);
                    charCount++;
                    spaceIndex = result.length() - 1;
                }
            }
            else{
                result.append(c);
                charCount++;
            }
            //---------------------------------------------------------------------
            // if we have reached minWidth, then insert a line break at last space
            //---------------------------------------------------------------------
            if(charCount > minWidth && spaceIndex > 0){
                result.setCharAt(spaceIndex, '\n');
                charCount = result.length() - spaceIndex;
                spaceIndex = 0;
            }
        }       
        result.append('\n');
        return result;
    }
    /**
     * The WrapText method constructs a new TextData object that includes newlines and spaces for tabs.
     * @param If prefix is specified, each line in the newly created TextData object begins with the prefix. Tabs in the prefix are replaced with spaces according to the value of TabWidth.
     * @param LineWidth The line width in characters
     * @param TabWidth The number of spaces to replace the tabs
     * @return The reformatted string.
     */
   
    public TextData wrapText(String prefix, int LineWidth, int TabWidth) {
        TextData result = new TextData(StringUtils.wrapText(this.theString.toString(), prefix, LineWidth, TabWidth));
        return result;
    }

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        else if (obj instanceof String) {
            return ((String)obj).equals(theString.toString());
        }
        else if (obj instanceof TextData) {
            return ((TextData) obj).theString.toString().equals(
                    theString.toString());
        }
        else {
            return false;
        }
    }
   
    @Override
    public int hashCode() {
      return theString.toString().hashCode();
    }

    public TextData append(Object obj) {
        theString.append(obj);
        reportChange();
        return this;
    }

    public TextData append(String str) {
        theString.append(str);
        reportChange();
        return this;
    }

    public TextData append(char c) {
        theString.append(c);
        reportChange();
        return this;
    }

    public TextData append(char str[]) {
        theString.append(str);
        reportChange();
        return this;
    }

    public TextData append(char str[], int offset, int len) {
        theString.append(str, offset, len);
        reportChange();
        return this;
    }

    public TextData append(boolean b) {
        theString.append(b);
        reportChange();
        return this;
    }

    public TextData append(double d) {
        theString.append(d);
        reportChange();
        return this;
    }

    public TextData append(float f) {
        theString.append(f);
        reportChange();
        return this;
    }

    public TextData append(int i) {
        theString.append(i);
        reportChange();
        return this;
    }

    public TextData append(long l) {
        theString.append(l);
        reportChange();
        return this;
    }

    public int capacity() {
        return theString.capacity();
    }

    public char charAt(int index) {
        return theString.charAt(index);
    }

    public void ensureCapacity(int minimumCapacity) {
        theString.ensureCapacity(minimumCapacity);
    }

    public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        theString.getChars(srcBegin, srcEnd, dst, dstBegin);
    }

    public TextData insert(int offset, Object obj) {
        theString.insert(offset, obj);
        reportChange();
        return this;
    }

    public TextData insert(int offset, String str) {
        theString.insert(offset, str);
        reportChange();
        return this;
    }

    public TextData insert(int offset, char c) {
        theString.insert(offset, c);
        reportChange();
        return this;
    }

    public TextData insert(int offset, char str[]) {
        theString.insert(offset, str);
        reportChange();
        return this;
    }

    public TextData insert(int offset, boolean b) {
        theString.insert(offset, b);
        reportChange();
        return this;
    }

    public TextData insert(int offset, double d) {
        theString.insert(offset, d);
        reportChange();
        return this;
    }

    public TextData insert(int offset, float f) {
        theString.insert(offset, f);
        reportChange();
        return this;
    }

    public TextData insert(int offset, int i) {
        theString.insert(offset, i);
        reportChange();
        return this;
    }

    public TextData insert(int offset, long l) {
        theString.insert(offset, l);
        reportChange();
        return this;
    }

    public int length() {
        return theString.length();
    }

    public TextData reverse() {
        theString.reverse();
        reportChange();
        return this;
    }

    public void setCharAt(int index, char ch) {
        theString.setCharAt(index, ch);
    }

    public void setLength(int newLength) {
        theString.setLength(newLength);
        if (theOffset > theString.length()) {
            theOffset = theString.length();
        }
    }

    public TextData getCopyData() {
        TextData lvReturn = new TextData(this);
        return lvReturn;
    }

    public int compareTo(Object o)
    {
        if (o == null) {
            GenericException errorVar = new GenericException("Object cannot be compared because it is null");
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
        if (o instanceof TextData) {
            int comp = this.compare((TextData)o);
            if (comp == 0) return 0;
            if (comp > 0) return 1;
            if (comp < 0) return -1;
        } else {
            ClassCastException errorVar = new ClassCastException("Object must be a sub-type of TextData");
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
        return 0;
    }

    public void propertyChange(PropertyChangeEvent arg0) {
      // -----------------------------------------------------------------------
      // TF:29/01/2010:JCT-640:Changed this to invoke a method on the superclass
      // -----------------------------------------------------------------------
        this.firePropertyChange("Value", null, this.getValue());

    }
    /**
     * Integer equivalent of text value.
     * @param i
     */
    public void setIntegerValue(int i){
        this.setValue(i);
    }
    /**
     * Double equivalent of text value.
     * @param i
     */
    public void setDoubleValue(double i){
        this.setValue(i);
    }
    /**
     * Boolean equivalent of text value.
     * @param i
     */
    public void setBooleanValue(boolean i){
        this.setValue(i);
    }

    /**
     * Compares the value attribute of the object specified in the source parameter with the value
     * of this object and returns an integer indicating their relative magnitude
     * (equals, less than, and so on). If the convert parameter is TRUE, the method
     * converts source to a DataValue object if necessary and then does the comparison.
     */
    public int compareValue(DataValue source, boolean convert, boolean ignoreCase) {
        if (source == null) {
            return Constants.DV_CMP_NIL;
        }
        else if (this.isNull() || source.isNull()) {
            return Constants.DV_CMP_NULL;
        }
        else if (source instanceof TextData) {
            String thisValue = this.getValue();
            String otherValue = ((TextData)source).getValue();
            if (ignoreCase) {
                int result = thisValue.compareToIgnoreCase(otherValue);
                return (result < 0) ? Constants.DV_CMP_LT :
                        (result == 0) ? Constants.DV_CMP_EQ :
                        Constants.DV_CMP_GT;
            }
            else {
                int result = thisValue.compareTo(otherValue);
                return (result < 0) ? Constants.DV_CMP_LT :
                        (result == 0) ? Constants.DV_CMP_EQ :
                        Constants.DV_CMP_GT;
            }
        }
        else if (convert) {
            try {
                String thisValue = this.getValue();
                String otherValue = source.getTextValue().getValue();
                if (ignoreCase) {
                    int result = thisValue.compareToIgnoreCase(otherValue);
                    return (result < 0) ? Constants.DV_CMP_LT :
                            (result == 0) ? Constants.DV_CMP_EQ :
                            Constants.DV_CMP_GT;
                }
                else {
                    int result = thisValue.compareTo(otherValue);
                    return (result < 0) ? Constants.DV_CMP_LT :
                            (result == 0) ? Constants.DV_CMP_EQ :
                            Constants.DV_CMP_GT;
                }
            }
            catch (Exception e) {
                return Constants.DV_CMP_UNCONVERTIBLE;
            }
        }
        return Constants.DV_CMP_UNCONVERTIBLE;
    }

    public boolean isNull() {
        return hasNullValue;
    }

    public boolean getIsNull() {
        return isNull();
    }
    /**
     * returns the current value of the TextData as an array of characters
     * @return
     */
    public char[] asCharArray() {
        char[] chars = new char[this.theString.length()];
      this.theString.getChars(0, this.theString.length(), chars, 0);
        return chars;
    }

    /**
     * returns the current value of the TextData as an array of bytes
     * @return
     */
    public byte[] asByteArray() {
        byte[] bytes = new byte[this.theString.length()];
        for (int i = 0; i < this.theString.length(); i++){
            bytes[i] = (byte)this.theString.charAt(i);
        }
        return bytes;
    }

    public int dataType() {
        return Constants.DV_DT_TEXT;
    }

    public void decodeValue(TextData text, DataFormat format) {
        if (format instanceof TextFormat)
            this.setValue(((TextFormat)format).decodeText(text));
    }

    /**
     * This method returns and instance of TextData with the value or returns a null
     * @param value
     * @return
     */
    public static TextData getInstance(String value){
        return (value == null) ? null : new TextData(value);
    }
   
    /**
     * Returns the value of the passed textdata, ie null if the passed value is null or else value.toString()
     * @param value
     * @return
     */
    public static String valueOf(TextData value) {
      return (value == null) ? null : value.toString();
    }
   
    /**
     * this method returns a TextData that is bound to an object property.
     * When the value of the TextData is changed, the bound property will also be changed.
     * @param object
     * @param property
     * @return
     */
    public static TextNullable bind(Object object, String property){
      // TF:24 sept. 2008:Changed this to use a text nullable, as this is needed for TextValue class
      // and is apparently what Forte returned
        if ((object == null)||(property == null)) {
            UsageException errorVar = new UsageException("Cannot bind TextData to property object or property is null");
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
        TextNullable result = new TextNullable();
        try {
            Object prop = PropertyUtils.getSimpleProperty(object, property);
            result.setValue((prop == null) ? null : prop.toString());
            result.beanProp = property;
            result.beanRef = object;
           
        } catch (IllegalAccessException e) {
            UsageException errorVar = new UsageException("Cannot bind TextData to property [" + property + "]", e);
            ErrorMgr.addError(errorVar);
            throw errorVar;
        } catch (InvocationTargetException e) {
            UsageException errorVar = new UsageException("Cannot bind TextData to property [" + property + "]", e);
            ErrorMgr.addError(errorVar);
            throw errorVar;
        } catch (NoSuchMethodException e) {
            UsageException errorVar = new UsageException("Cannot bind TextData to property [" + property + "]", e);
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
        return result;
    }

    public static TextData map(Object object, String property){
        if ((object == null)||(property == null)) {
            UsageException errorVar = new UsageException("Cannot bind TextData to property object or property is null");
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
        return new MappedTextData(object, property);
    }
   
    // TF:08/04/2009:Commented out the finalize method as it slows things down, and doesn't seem to be needed. The only time
    // the propery connector is used is in the map() method above, and that now gets its own finalizable class.
//  @Override
//  protected void finalize() throws Throwable {
//    super.finalize();
//    if (this.propConnector != null) {
//      this.propConnector.release();
//    }
//  }

  /**
     * @Deprecated Undocumented Forte method that returns the length of the String stored in the TextData. Use {@link #length()}
     * @return
     */
    public int totalLength() {
        return length();
    }
    /**
     * returns the index of the target string.
     * @param locator
     * @return index of the string
     */
    //PM:27/3/08
  public int indexOf(TextData locator) {
    return this.theString.indexOf(locator.toString());
  }
 
  //PM:20/05/2008:
  public int indexOf(String string) {
    return this.theString.indexOf(string);
  }
    @Override
    // AD:Aug 15, 2008 - Override DataValue for more efficient setting if of the same type
    public void setValue(DataValue source) {
    if (source instanceof TextData) {
      setValue((TextData) source);
    } else {
      super.setValue(source);
    }
  }
 
    public static void main(String[] args) {
      DoubleData x = new DoubleData();
      x.setValue(0.714285714289872);
      TextData y = new TextData();
      y.concat(x);
      System.out.println(x);
      System.out.println(y);
      x.multiply(x, 7);
      y = new TextData();
      y.concat(x);
      System.out.println(x);
      System.out.println(y);
      x.setValue(123.4567); System.out.println(x);
      x.setValue(-123.4567); System.out.println(x);
      x.setValue(1.234567); System.out.println(x);
      x.setValue(-1.234567); System.out.println(x);
      x.setValue(1.234549); System.out.println(x);
      x.setValue(-1.234549); System.out.println(x);
      x.setValue(1.23456); System.out.println(x);
      x.setValue(-1.23456); System.out.println(x);
      x.setValue(1.234566); System.out.println(x);
      x.setValue(-1.234566); System.out.println(x);
      x.setValue(1.234565); System.out.println(x);
      x.setValue(-1.234565); System.out.println(x);
      x.setValue(1.234564); System.out.println(x);
      x.setValue(-1.234564); System.out.println(x);
      x.setValue(12345.05); System.out.println(x);
      x.setValue(-12345.05); System.out.println(x);
      x.setValue(12345.051); System.out.println(x);
      x.setValue(-12345.05); System.out.println(x);
      x.setValue(9999999); System.out.println(x);
     
      TextNullable ss = new TextNullable();
      ss.setIsNull(true);
      System.out.println(ss.getValue());
      System.out.println(ss.getActualSize());
      System.out.println(ss.getTextValue().getValue());
      System.out.println(ss.getTextValue().getActualSize());
    }
}
TOP

Related Classes of net.helipilot50.stocktrade.framework.TextData

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.