# Text.java

    /*
    ** Module   : Text.java
    ** Abstract : Base class for P2J's longchar and character implementations.
    **
    ** Copyright (c) 2013-2025, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --------------------------------Description---------------------------------
    ** 001 CA  20130130 Created initial version by extracting the internals from the character
    **                  class.
    ** 002 GES 20130322 Fixed the BDT c'tor and modified the assign(BDT,boolean) to be the
    **                  common location for the _POLY morphing. This is safe because all current
    **                  usage would have generated a ClassCastException if that method was ever used
    **                  with an invalid type. Now, instead of the CCE, the code will morph the value
    **                  as is possible. The morphing code still needs to be finished.
    ** 003 CA  20131013 Added no-op deleted() method, required by the changes in Finalizable interface.
    ** 004 ECF 20140417 Implemented quick fix for missing _POLY support.
    ** 005 OM  20141014 Improved _POLY support for assign(Undoable).
    ** 006 GES 20141003 Fixed embedded null character behavior. Construction of a new instance or
    **                  assignment from strings that have embedded nulls is disallowed.  Construction
    **                  or assignment from existing instances that have null characters is allowed
    **                  if the subclass allows it.  A special setter is provided for explicit use
    **                  in forcing assignment of string data that has embedded nulls. That is the
    **                  only way null data can get into an instance. Some helper methods were moved
    **                  here from a subclass.
    ** 007 ECF 20150715 Replace StringBuffer with StringBuilder.
    ** 008 OM  20150924 Added missing replace() overloads.
    ** 009 ECF 20151111 Performance fix: in assign(Text, boolean), defer expensive null character scan
    **                  until after we determine whether embedded nulls are allowed; common case
    **                  allows them.
    ** 010 ECF 20160227 Fixed heading numbers.
    ** 011 ECF 20160430 Optimized compareTo and equalsCaseInsensitively to avoid costly string
    **                  comparisons if the internal String objects are the same instance.
    ** 012 OM  20160603 Fixed rtrimming to space character only.
    ** 013 GES 20160617 Fixed unknown literal processing in assign() for processing polymorphic
    **                  return values.
    ** 014 ECF 20160614 Implemented faster copy constructor.
    ** 015 CA  20160627 Reworked undoable support - the undoables register themselves with all blocks
    **                  up the stack, until either 1. the tx block which created it is reached or
    **                  2. the last tx block where it was saved is reached.
    ** 016 SBI 20160915 Fixed an extra delimiter that was added by replaceEntry.
    ** 017 IAS 20160919 Serialization optimization.
    ** 018 SBI 20160922 Fixed NPE if ENTRY is used for an unknown value.
    ** 019 CA  20161004 Fixed direct comparison with unknown values.
    ** 020 EVL 20171108 Changed read and write approach for String value from using (read|write)UTF()
    **                  to (read|write)Object() to properly handle Windows Unicode strings.
    ** 021 GES 20171206 Removed explicit bypass on pending error (new silent error mode).
    ** 022 CA  20190508 Added support for CASE-SENSITIVE extent var option.
    ** 023 SVL 20190614 Added assign(clob) to prevent abend during dynamic tables compilation.
    ** 024 CA  20190927 Added support for direct Java access from 4GL code.
    ** 025 CA  20191203 Clear the value when setting to unknown.
    ** 026 CA  20200506 Fixed runtime for character vs longchar OO method calls.
    ** 027 ME  20200528 Add assign for "legacy" object - toLegacyString.
    ** 028 IAS 20200908 Rework (de)serialization.
    ** 029 EVL 20200922 Replace Java split() with our implementation for TextOps.splitInt().
    **     VVT 20201001 javaTruncateNull() optimized.
    **     ME  20210504 Added isIncompatibleTypesOnConversion override for invalid POLY conversion.
    **     CA  20210609 Reworked INPUT/INPUT-OUTPUT parameters to a new approach, where they are explicitly
    **                  initialized at the method's execution, and not at the caller's arguments.
    **     AL2 20220319 Added value proxy check in assign.
    **     ME  20220404 Right trim string for hashCode value.
    ** 030 HC  20230118 Eliminated some of the uses of String.toUpperCase and/or String.toLowerCase
    **                  for performance.
    ** 031 EVL 20230317 Adding method to get internal Text object length.  In some conditions for longchar this
    **                  can be different than simple length of the backend String variable.
    ** 032 CA  20240324 Short-circuit compareTo when one or both the operands are empty.
    ** 033 CA  20240423 Optimized 'replaceEntry' to create a minimal number of Strings.
    ** 034 SB  20240530 Use the string value of a BinaryData instance for assignment, therefore accounting
    **                  for charset. Refs #8783.
    ** 035 CA  20241030 Avoid 'Text.javaSpacifyNull' if the string is known to already have been processed.
    ** 036 AS  20241205 Introduced valueEquals method.
    ** 037 FER 20250139 Modified compareTo() method to return '-1' if this value is null regardless of the type
    **                  of the obj to be compared with. Otherwise it would lead to NPE.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.util;

    import static com.goldencode.util.NativeTypeSerializer.*;
    import java.io.*;
    import java.nio.charset.Charset;
    import com.goldencode.util.*;

    /**
     * Base class for the {@link character} and {@link longchar} data types.
     */
    public abstract class Text
    extends BaseDataType
    {
       /** Charset name to use for encoding or <code>null</code> for default. */
       protected static final String override = Utils.getCharsetOverride();

       /**
        * Stores the state of this character as an instance of class
        * <code>String</code>.  This is independent of the state of whether or
        * not this instance represents the <code>unknown value</code>.
        */
       protected String value = null;

       /**
        * Stores the state of whether or not this instance represents the
        * <code>unknown value</code>, independent of the state of this
        * character's data.
        */
       protected boolean unknown = false;

       /**
        * Stores the state of whether or not this instance compares case
        * sensitively.
        */
       protected boolean caseSens = false;

       /** Flag indicating if {@link #javaSpacifyNull(String)} should execute. */
       protected boolean javaSpacifyNull = true;

       /**
        * Default constructor, creates an instance that represents the unknown
        * value.
        */
       public Text()
       {
          unknown = true;
       }

       /**
        * This is a special c'tor which should be used only when converting the
        * value returned by a function or method with polymorphic return type into the
        * expected type (i.e. DYNAMIC-FUNCTION()).  In such cases, the 4GL does some
        * automatic type conversion (see {@link #assign(BaseDataType)}).
        *
        * @param    value
        *           The value to be used for this instance.
        */
       public Text(BaseDataType value)
       {
          // the value may be a proxy; make sure to unwrap and work with the real value
          value = value == null ? null : value.val();
          if (value == null || value.isUnknown())
          {
             setUnknown();
          }
          else
          {
             assign(value);
          }
       }

       /**
        * Constructs an instance that exactly matches that of the passed-in instance.
        *
        * @param    value
        *           The value of the resulting instance.
        */
       public Text(Text value)
       {
          // the value may be a proxy; make sure to unwrap and work with the real value
          if (BaseDataType.isProxy(value))
          {
             if (value.val() instanceof Text)
             {
                value = (Text) value.val();
             }
             else
             {
                assign(value.val());
                return;
             }
          }
          deepAssign(value);
       }

       /**
        * Constructs an instance that has the <code>String</code> value passed in and which
        * <b>does not</b> represent the <code>unknown value</code>, unless the <code>value</code>
        * argument is <code>null</code>.
        * <p>
        * Strings (especially converted literals) can have embedded null characters, but in the 4GL
        * the resulting variable instances are "spacified".  The given value will be processed by
        * {@link #javaSpacifyNull} before storing it in the new instance.
        *
        * @param    value
        *           The character value of the resulting instance.
        */
       public Text(String value)
       {
          if (value == null)
          {
             setUnknown();
          }
          else
          {
             // protected against embedded null characters
             this.value = javaSpacifyNull(value);
             this.javaSpacifyNull = false;
          }
       }

       /**
        * Constructs an instance that has the <code>String</code> value passed in and which
        * <b>does not</b> represent the <code>unknown value</code>, unless the <code>value</code>
        * argument is <code>null</code>.
        * <p>
        * Strings (especially converted literals) can have embedded null characters, but in the 4GL
        * the resulting variable instances are "spacified".  The given value will be processed by
        * {@link #javaSpacifyNull} before storing it in the new instance.
        *
        * @param    value
        *           The character value of the resulting instance.
        * @param    caseSens
        *           Specifies whether this instance should use case sensitive
        *           comparisons.
        */
       public Text(String value, boolean caseSens)
       {
          this(value, caseSens, true);
       }

       /**
        * Constructs an instance that has the <code>String</code> value passed in and which
        * <b>does not</b> represent the <code>unknown value</code>, unless the <code>value</code>
        * argument is <code>null</code>.
        * <p>
        * Strings (especially converted literals) can have embedded null characters, but in the 4GL
        * the resulting variable instances are "spacified".  The given value will be processed by
        * {@link #javaSpacifyNull} before storing it in the new instance.
        *
        * @param    value
        *           The character value of the resulting instance.
        * @param    caseSens
        *           Specifies whether this instance should use case sensitive
        *           comparisons.
        * @param    javaSpacifyNull
        *           Flag indicating if {@link #javaSpacifyNull(String)} needs to be executed on the value or not.
        */
       public Text(String value, boolean caseSens, boolean javaSpacifyNull)
       {
          if (value == null)
          {
             setUnknown();
          }
          else
          {
             // protected against embedded null characters
             this.value    = javaSpacifyNull ? javaSpacifyNull(value) : value;
             this.caseSens = caseSens;
             this.javaSpacifyNull = javaSpacifyNull;
          }
       }

       /**
        * Constructs an instance that has a <code>char</code> value passed in and which
        * <b>does not</b> represent the <code>unknown value</code>.
        * <p>
        * If the given value is the null character, an ASCII space character will be assigned.
        *
        * @param    ch
        *           The character value of the resulting instance.
        */
       public Text(char ch)
       {
          // protected against embedded null characters
          this.value = (ch == '\0') ? " " : new StringBuilder().append(ch).toString();
          javaSpacifyNull = false;
       }

       /**
        * A method that checks the equality of two Text parameters.
        * The comparison of the String values is low level, it ignores
        * case sensitivity and trailing/leading spaces.
        *
        * @param   op1
        *          The first operand of the comparison.
        * @param   op2
        *          The second operand of the comparison
        *
        * @return  True if the operands have the same String value.
        */
       public static boolean valueEquals(Text op1, Text op2)
       {
          if (op1.unknown == op2.unknown && op1.unknown)
          {
             return true;
          }
          if (op1.unknown != op2.unknown)
          {
             return false;
          }
          return op1.value.equals(op2.value);
       }

       /**
        * Set the CASE-SENSITIVE option for all elements in this array.
        *
        * @param    list
        *           The character or longchar array.
        * @param    caseSens
        *           Flag indicating if the CASE-SENSITIVE option is on or off.
        */
       public static void setCaseSensitive(Text[] list, boolean caseSens)
       {
          for (Text item : list)
          {
             item.setCaseSensitive(caseSens);
          }

          // set the decimal precision, for lengths > 0 the precision is stored
          // in the array components
          if (list.length == 0)
          {
             ArrayAssigner.setCaseSensitive(list, caseSens);
          }
       }

       /**
        * Return a string which contains the contents of the given string up to
        * (but not including) the first <code>null</code> character (ASCII 0x00)
        * and for that <code>null</code> character and all following characters
        * the resulting string will contain spaces.
        * <p>
        * The resulting length of the output string will be the same as the
        * length of the input string.
        * <p>
        * This processing is needed by some operators and/or functionality that
        * process <code>character</code> data.
        * <p>
        * This transformation is for runtime strings, NOT for Progress compatible
        * string literals.  If a source code conversion is needed, please use
        * {@link #progressSpacifyNull}.
        * <p>
        * As an example, a string such as:
        * <p>
        * <pre>
        * Input                               Output
        * ----------------------------------  ----------------------------------
        * "This has a \0 (null) char."        "This has a               "
        * "\0"                                " "
        * </pre>
        *
        * @param    data
        *           The input string (not valid for encoding in a source file).
        *
        * @return   The "space-ified" output string (still not valid for
        *           encoding in a source file).
        */
       public static String javaSpacifyNull(String data)
       {
          if (data.isEmpty())
          {
             return data;
          }

          StringBuilder sb = null;

          int idx = data.indexOf('\0');

          if (idx != -1)
          {
             sb = new StringBuilder(data.substring(0, idx));
             StringHelper.repeatChar(' ', (data.length() - idx), sb);
          }

          return sb == null ? data : sb.toString();
       }

       /**
        * Return a string which contains the contents of the given string up to
        * (but not including) the first <code>null</code> character (ASCII 0x00),
        * the resulting string is truncated at the first <code>null</code>.
        * <p>
        * As an example, a string such as:
        * <p>
        * <pre>
        * Input                               Output
        * ----------------------------------  ----------------------------------
        * "This has a \0 (null) char."        "This has a "
        * "\0"                                ""
        * </pre>
        *
        * @param    data
        *           The input string (not valid for encoding in a source file).
        *
        * @return   The truncated output string (still not valid for
        *           encoding in a source file).
        */
       public static String javaTruncateNull(String data)
       {
          int idx = data.indexOf('\0');

          if (idx == -1)
          {
             return data;
          }

          return data.substring(0, idx);
       }

       /**
        * Return a string which contains the contents of the given string up to
        * (but not including) the first <code>null</code> character (encoded as a
        * Progress escape sequence) and for that <code>null</code> character and
        * all following characters the resulting string will contain spaces.
        * <p>
        * The resulting length of the string (in terms of the number of
        * characters that the string will include at runtime) will NOT change,
        * although the length of the string representation (as a source code
        * representation stored in a Java string) may be reduced as the
        * <code>null</code> character's escape sequence (which would require 4
        * characters to represent as in <code>~000</code>) and any following
        * escaped sequences will be converted to a space which only consumes
        * a single character position.
        * <p>
        * The enclosing single or double quote characters are maintained (not
        * overwritten).
        * <p>
        * This processing is needed by the Progress preprocessor and other
        * conversion processing of string literals.
        * <p>
        * This transformation is for Progress compatible string literals, NOT for
        * runtime strings.  If a runtime string conversion is needed, please use
        * {@link #javaSpacifyNull}.  Both forms of the <code>null</code>
        * character escape sequence are handled.
        * <p>
        * As an example, a Progress string such as:
        * <p>
        * <pre>
        * Input                               Output
        * ----------------------------------  ----------------------------------
        * "This has a ~000 (null) char."      "This has a               "
        * 'This has a ~000 (null) char.'      'This has a               '
        * "~000"                              " "
        * '~000'                              ' '
        * "\000"                              " "
        * '\000'                              ' '
        * </pre>
        *
        * @param    data
        *           A Progress source code representation of a string literal
        *           which is the input string to be transformed.
        * @param    unixEsc
        *           <code>true</code> if the backslash character should be
        *           treated as an escape character.
        *
        * @return   The "space-ified" output string (still valid as a Progress
        *           string literal that could be output into source code).
        */
       public static String progressSpacifyNull(String data, boolean unixEsc)
       {
          StringBuilder sb = new StringBuilder();

          // put the leading single or double quote char into the output buffer
          char quote = data.charAt(0);
          sb.append(quote);

          // only scan up to the trailing quote char
          int end = data.length() - 1;

          boolean eat = false;

          // loop through the contents
          for (int i = 1; i < end; i++)
          {
             char current = data.charAt(i);
             char next;

             if (current == '~' || (current == '\\' && unixEsc))
             {
                if (!eat)
                {
                   // flow through mode

                   // check for the null escape sequence
                   String esc = ((i + 4) <= end) ? data.substring(i + 1, i + 4) : null;

                   if (esc != null && esc.equals("000"))
                   {
                      // the first null, shift into space-ify mode
                      eat = true;
                      i += 3;
                      sb.append(' ');
                   }
                   else
                   {
                      // some other escape sequence, allow it to flow through
                      sb.append(current);

                      // there must always be a following character, if it is
                      // a doubled escape char, we must handle it here to avoid
                      // dropping back through this code since it is not the
                      // opening of a new escape sequence (since all following
                      // chars can be handled the same way we just go ahead
                      // no matter which one it is)
                      i++;
                      sb.append(readChar(data, i));
                   }
                }
                else
                {
                   // space-ify mode (eat the full escape sequence here)

                   // output the replacement (always 1 char)
                   sb.append(' ');

                   // there must always be at least 1 following character
                   i++;
                   next = readChar(data, i);

                   if (next == 'b'  || next == 't'  || next == 'n'  ||
                       next == 'f'  || next == 'r'  || next == '\"' ||
                       next == '\\' || next == '~'  || next == 'E')
                   {
                      // these are only 1 char in size, so we are done
                   }
                   else if (next >= '0' && next <= '7')
                   {
                      // eat the next 2 chars too because this is an octal
                      // escape sequence
                      i += 2;
                   }
                }
             }
             else
             {
                sb.append(eat ? ' ' : current);
             }
          }

          // add the trailing (close) quote char
          sb.append(data.charAt(end));

          return sb.toString();
       }

       /**
        * Test for certain failure conditions and if OK return the indexed character, otherwise throw
        * an exception with useful debugging information.
        *
        * @param    input
        *           The string to read from.
        * @param    index
        *           The 0-based index into the string, from which to read the character.
        *
        * @return   The character at the specified index.
        *
        * @throws   IllegalArgumentException
        *           If the index is out of bounds.
        */
       public static char readChar(String input, int index)
       throws IllegalArgumentException
       {
          char next;

          if (index == input.length())
          {
             throw new IllegalArgumentException("Unexpected end of string.");
          }

          try
          {
             next = input.charAt(index);
          }
          catch (IndexOutOfBoundsException iob)
          {
             throw new IllegalArgumentException("Invalid index (" + index +
                                                " in string \"" + input + "\").");
          }

          return next;
       }

       /**
        * Report if existing instances that have embedded null characters are allowed to be
        * assigned to this instance.
        *
        * @return   Always <code>true</code>. Sub-classes can override this to change the
        *           behavior.
        */
       public boolean isEmbeddedNullAllowed()
       {
          return true;
       }

       /**
        * Returns the larger (lexicographically) of the current or passed-in
        * instance.
        *
        * @param    c
        *           The instance to compare against.
        *
        * @return   The largest instance.
        */
       public Text maximum(Text c)
       {
          return (Text) super.maximum(c);
       }

       /**
        * Returns the smaller (lexicographically) of the current or passed-in
        * instance.
        *
        * @param    c
        *           The instance to compare against.
        *
        * @return   The smallest instance.
        */
       public Text minimum(Text c)
       {
          return (Text) super.minimum(c);
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(Text text, double pos)
       throws ErrorConditionException
       {
          replaceWorker(text, new integer(pos), new integer(-1), true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(Text text, double pos, double len)
       throws ErrorConditionException
       {
          replaceWorker(text, new integer(pos), new integer(len), true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        * @param    type
        *           Description of how to interpret the position and length values.
        *           At this time only "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(Text text, double pos, double len, String type)
       throws ErrorConditionException
       {
          checkOverlayType(type);
          replaceWorker(text, new integer(pos), new integer(len), true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(Text text, double pos, NumberType len)
       throws ErrorConditionException
       {
          replaceWorker(text, new integer(pos), len, true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        * @param    type
        *           Description of how to interpret the position and length values.
        *           At this time only "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(Text  text, double pos, NumberType len, String type)
       throws ErrorConditionException
       {
          checkOverlayType(type);
          replaceWorker(text, new integer(pos), len, true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(Text text, NumberType pos)
       throws ErrorConditionException
       {
          replaceWorker(text, pos, new integer(-1), true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(Text text, NumberType pos, double len)
       throws ErrorConditionException
       {
          replaceWorker(text, pos, new integer(len), true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        * @param    type
        *           Description of how to interpret the position and length values.
        *           At this time only "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(Text text, NumberType pos, double len, String type)
       throws ErrorConditionException
       {
          checkOverlayType(type);
          replaceWorker(text, pos, new integer(len), true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(Text text, NumberType pos, NumberType len)
       throws ErrorConditionException
       {
          replaceWorker(text, pos, len, true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        * @param    type
        *           Description of how to interpret the position and length values.
        *           At this time only "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(Text text, NumberType pos, NumberType len, String type)
       throws ErrorConditionException
       {
          checkOverlayType(type);
          replaceWorker(text, pos, len, true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(String text, double pos)
       throws ErrorConditionException
       {
          replaceWorker(new character(text),
                        new integer(pos),
                        new integer(-1),
                        true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(String text, double pos, double len)
       throws ErrorConditionException
       {
          replaceWorker(new character(text),
                        new integer(pos),
                        new integer(len),
                        true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        * @param    type
        *           Description of how to interpret the position and length values.
        *           At this time only "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(String text, double pos, double len, String type)
       throws ErrorConditionException
       {
          checkOverlayType(type);
          replaceWorker(new character(text),
                        new integer(pos),
                        new integer(len),
                        true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(String text, double pos, NumberType len)
       throws ErrorConditionException
       {
          replaceWorker(new character(text), new integer(pos), len, true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        * @param    type
        *           Description of how to interpret the position and length values.
        *           At this time only "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(String text, double pos, NumberType len, String type)
       throws ErrorConditionException
       {
          checkOverlayType(type);
          replaceWorker(new character(text), new integer(pos), len, true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(String text, NumberType pos)
       throws ErrorConditionException
       {
          replaceWorker(new character(text), pos, new integer(-1), true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(String text, NumberType pos, double len)
       throws ErrorConditionException
       {
          replaceWorker(new character(text), pos, new integer(len), true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        * @param    type
        *           Description of how to interpret the position and length values.
        *           At this time only "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(String text, NumberType pos, double len, String type)
       throws ErrorConditionException
       {
          checkOverlayType(type);
          replaceWorker(new character(text), pos, new integer(len), true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(String text, NumberType pos, NumberType len)
       throws ErrorConditionException
       {
          replaceWorker(new character(text), pos, len, true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace
        *           and the number of characters from the replacement text to
        *           include in the replacement.
        * @param    type
        *           Description of how to interpret the position and length values.
        *           At this time only "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text overlay(String     text,
                           NumberType pos,
                           NumberType len,
                           String     type)
       throws ErrorConditionException
       {
          checkOverlayType(type);
          replaceWorker(new character(text), pos, len, true);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(Text text, double pos)
       throws ErrorConditionException
       {
          replaceWorker(text, new integer(pos), new integer(-1), false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(Text text, double pos, double len)
       throws ErrorConditionException
       {
          replaceWorker(text, new integer(pos), new integer(len), false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(Text text, double pos, NumberType len)
       throws ErrorConditionException
       {
          replaceWorker(text, new integer(pos), len, false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(Text text, NumberType pos)
       throws ErrorConditionException
       {
          replaceWorker(text, pos, new integer(-1), false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(Text text, NumberType pos, double len)
       throws ErrorConditionException
       {
          replaceWorker(text, pos, new integer(len), false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(Text text, NumberType pos, NumberType len)
       throws ErrorConditionException
       {
          replaceWorker(text, pos, len, false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(String text, double pos)
       throws ErrorConditionException
       {
          replaceWorker(new character(text),
                        new integer(pos),
                        new integer(-1),
                        false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(String text, double pos, double len)
       throws ErrorConditionException
       {
          replaceWorker(new character(text),
                        new integer(pos),
                        new integer(len),
                        false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(String text, double pos, NumberType len)
       throws ErrorConditionException
       {
          replaceWorker(new character(text), new integer(pos), len, false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(String text, NumberType pos)
       throws ErrorConditionException
       {
          replaceWorker(new character(text), pos, new integer(-1), false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(String text, NumberType pos, double len)
       throws ErrorConditionException
       {
          replaceWorker(new character(text), pos, new integer(len), false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(String text, NumberType pos, NumberType len)
       throws ErrorConditionException
       {
          replaceWorker(new character(text), pos, len, false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the passed text at the
        * 1-based index into the string. The entire replacement text will be placed into the
        * resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement is to occur.
        * @param    type
        *           Specifies whether this substring operates on "CHARACTER" (default), "RAW" or
        *           "COLUMN".  At this time "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(Text text, double pos, String type)
       throws ErrorConditionException
       {
          replaceWorker(text, new integer(pos), new integer(-1), false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the passed text at the
        * 1-based index into the string.  The entire replacement text will be placed into the
        * resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        * @param    type
        *           Specifies whether this substring operates on "CHARACTER" (default), "RAW" or
        *           "COLUMN".  At this time "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(Text text, double pos, double len, String type)
       throws ErrorConditionException
       {
          replaceWorker(text, new integer(pos), new integer(len), false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the passed text at the
        * 1-based index into the string.  The entire replacement text will be placed into the
        * resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        * @param    type
        *           Specifies whether this substring operates on "CHARACTER" (default), "RAW" or
        *           "COLUMN".  At this time "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(Text text, double pos, NumberType len, String type)
       throws ErrorConditionException
       {
          replaceWorker(text, new integer(pos), len, false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement is to occur.
        * @param    type
        *           Specifies whether this substring operates on "CHARACTER" (default), "RAW" or
        *           "COLUMN".  At this time "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(Text text, NumberType pos, String type)
       throws ErrorConditionException
       {
          replaceWorker(text, pos, new integer(-1), false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        * @param    type
        *           Specifies whether this substring operates on "CHARACTER" (default), "RAW" or
        *           "COLUMN".  At this time "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(Text text, NumberType pos, double len, String type)
       throws ErrorConditionException
       {
          replaceWorker(text, pos, new integer(len), false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        * @param    type
        *           Specifies whether this substring operates on "CHARACTER" (default), "RAW" or
        *           "COLUMN".  At this time "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(Text text, NumberType pos, NumberType len, String type)
       throws ErrorConditionException
       {
          replaceWorker(text, pos, len, false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement is to occur.
        * @param    type
        *           Specifies whether this substring operates on "CHARACTER" (default), "RAW" or
        *           "COLUMN".  At this time "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(String text, double pos, String type)
       throws ErrorConditionException
       {
          replaceWorker(new character(text),
                        new integer(pos),
                        new integer(-1),
                        false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        * @param    type
        *           Specifies whether this substring operates on "CHARACTER" (default), "RAW" or
        *           "COLUMN".  At this time "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(String text, double pos, double len, String type)
       throws ErrorConditionException
       {
          replaceWorker(new character(text),
                        new integer(pos),
                        new integer(len),
                        false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        * @param    type
        *           Specifies whether this substring operates on "CHARACTER" (default), "RAW" or
        *           "COLUMN".  At this time "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(String text, double pos, NumberType len, String type)
       throws ErrorConditionException
       {
          replaceWorker(new character(text), new integer(pos), len, false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement is to occur.
        * @param    type
        *           Specifies whether this substring operates on "CHARACTER" (default), "RAW" or
        *           "COLUMN".  At this time "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(String text, NumberType pos, String type)
       throws ErrorConditionException
       {
          replaceWorker(new character(text), pos, new integer(-1), false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        * @param    type
        *           Specifies whether this substring operates on "CHARACTER" (default), "RAW" or
        *           "COLUMN".  At this time "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(String text, NumberType pos, double len, String type)
       throws ErrorConditionException
       {
          replaceWorker(new character(text), pos, new integer(len), false);
          return this;
       }

       /**
        * Overwrites characters in the current value of this instance with the
        * passed text at the 1-based index into the string.  The entire
        * replacement text will be placed into the resulting value.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        * @param    type
        *           Specifies whether this substring operates on "CHARACTER" (default), "RAW" or
        *           "COLUMN".  At this time "character" is supported.
        *
        * @return   The current instance is returned.
        * @throws ErrorConditionException
        */
       public Text replace(String text, NumberType pos, NumberType len, String type)
       throws ErrorConditionException
       {
          replaceWorker(new character(text), pos, len, false);
          return this;
       }

       /**
        * Replaces the specified 1-based indexed entry in the current instance
        * which must be a delimited list.
        *
        * @param    ii
        *           The 1-based index into the list.
        * @param    delimit
        *           The delimiter character.
        * @param    replace
        *           The replacment text.
        *
        * @return   The current instance.
        * @throws ErrorConditionException
        */
       public Text replaceEntry(double ii, char delimit, String replace)
       throws ErrorConditionException
       {
          if (isUnknown())
          {
             return this;
          }

          // convert 1 based index to 0 based
          int i   = (int) ii;
          int idx = i - 1;

          // split up the list (make sure all possible entries are included,
          // even empty trailing entries --> use -1 as the limit to force this)
          // using our own implementation of the split, which returns a 3-element array, with:
          // - prefix (everything before the found element, will end with the delimiter)
          // - the element at the given position
          // - suffix (everything after the found element, will not start with the delimiter)
          String[] array = TextOps.splitInt(value, delimit, idx, false);

          if (array == null)
          {
             TextOps.genOutsideRangeError(i, value);
             return this;
          }

          StringBuilder sb = new StringBuilder();

          if (array[0] != null)
          {
             // this already has the delimiter
             sb.append(array[0]);
          }
          sb.append(replace);
          if (array[2] != null)
          {
             sb.append(delimit).append(array[2]);
          }

          String newVal = sb.toString();

          checkUndoable(() -> new character(newVal));

          // save the result; does not need null character protection since the data is already
          // coming from an existing text instance of the same type
          value = newVal;

          return this;
       }

       /**
        * Replaces the specified 1-based indexed comma-delimited entry in the
        * current instance which must be a delimited list.
        *
        * @param    i
        *           The 1-based index into the list.
        * @param    replace
        *           The replacment text.
        *
        * @return   The current instance.  It may have been set to the
        *           <code>unknown value</code> if any of the inputs are
        *           <code>unknown value</code>.
        * @throws ErrorConditionException
        */
       public Text replaceEntry(double i, Text replace)
       throws ErrorConditionException
       {
          if (replace.unknown)
          {
             setUnknown();
             return this;
          }

          return replaceEntry((int) i, ',', replace.value);
       }

       /**
        * Replaces the specified 1-based indexed entry in the current instance
        * which must be a delimited list.
        *
        * @param    i
        *           The 1-based index into the list.
        * @param    delimit
        *           The delimiter character.
        * @param    replace
        *           The replacment text.
        *
        * @return   The current instance.  It may have been set to the
        *           <code>unknown value</code> if any of the inputs are
        *           <code>unknown value</code>.
        * @throws ErrorConditionException
        */
       public Text replaceEntry(double i, character delimit, Text replace)
       throws ErrorConditionException
       {
          if (replace.unknown || delimit.unknown)
          {
             setUnknown();
             return this;
          }

          return replaceEntry((int) i,
                              TextOps.safeDelimiter(delimit.value),
                              replace.value);
       }

       /**
        * Replaces the specified 1-based indexed entry in the current instance
        * which must be a delimited list.
        *
        * @param    i
        *           The 1-based index into the list.
        * @param    delimit
        *           The delimiter character.
        * @param    replace
        *           The replacment text.
        *
        * @return   The current instance.  It may have been set to the
        *           <code>unknown value</code> if any of the inputs are
        *           <code>unknown value</code>.
        * @throws ErrorConditionException
        */
       public Text replaceEntry(double i, character delimit, String replace)
       throws ErrorConditionException
       {
          if (delimit.unknown)
          {
             setUnknown();
             return this;
          }

          return replaceEntry((int) i,
                              TextOps.safeDelimiter(delimit.value),
                              replace);
       }

       /**
        * Replaces the specified 1-based indexed comma-delimited entry in the
        * current instance which must be a delimited list.
        *
        * @param    i
        *           The 1-based index into the list.
        * @param    replace
        *           The replacment text.
        *
        * @return   The current instance.
        * @throws ErrorConditionException
        */
       public Text replaceEntry(double i, String replace)
       throws ErrorConditionException
       {
          return replaceEntry((int) i, ',', replace);
       }

       /**
        * Replaces the specified 1-based indexed entry in the current instance
        * which must be a delimited list.
        *
        * @param    i
        *           The 1-based index into the list.
        * @param    delimit
        *           The delimiter character.
        * @param    replace
        *           The replacment text.
        *
        * @return   The current instance.  It may have been set to the
        *           <code>unknown value</code> if any of the inputs are
        *           <code>unknown value</code>.
        * @throws ErrorConditionException
        */
       public Text replaceEntry(double i, String delimit, Text replace)
       throws ErrorConditionException
       {
          if (replace.unknown)
          {
             setUnknown();
             return this;
          }

          return replaceEntry((int) i,
                              TextOps.safeDelimiter(delimit),
                              replace.value);
       }

       /**
        * Replaces the specified 1-based indexed entry in the current instance
        * which must be a delimited list.
        *
        * @param    i
        *           The 1-based index into the list.
        * @param    delimit
        *           The delimiter character.
        * @param    replace
        *           The replacment text.
        *
        * @return   The current instance.
        * @throws ErrorConditionException
        */
       public Text replaceEntry(double i, String delimit, String replace)
       throws ErrorConditionException
       {
          return replaceEntry((int) i, TextOps.safeDelimiter(delimit), replace);
       }

       /**
        * Replaces the specified 1-based indexed comma-delimited entry in the
        * current instance which must be a delimited list.
        *
        * @param    i
        *           The 1-based index into the list.
        * @param    replace
        *           The replacment text.
        *
        * @return   The current instance.  It may have been set to the
        *           <code>unknown value</code> if any of the inputs are
        *           <code>unknown value</code>.
        * @throws ErrorConditionException
        */
       public Text replaceEntry(NumberType i, character replace)
       throws ErrorConditionException
       {
          if (i.isUnknown() || replace.unknown)
          {
             setUnknown();
             return this;
          }

          return replaceEntry(i.intValue(), ',', replace.value);
       }

       /**
        * Replaces the specified 1-based indexed entry in the current instance
        * which must be a delimited list.
        *
        * @param    i
        *           The 1-based index into the list.
        * @param    delimit
        *           The delimiter character.
        * @param    replace
        *           The replacment text.
        *
        * @return   The current instance.  It may have been set to the
        *           <code>unknown value</code> if any of the inputs are
        *           <code>unknown value</code>.
        * @throws ErrorConditionException
        */
       public Text replaceEntry(NumberType i, character delimit, Text replace)
       throws ErrorConditionException
       {
          if (i.isUnknown() || replace.unknown || delimit.unknown)
          {
             setUnknown();
             return this;
          }

          return replaceEntry(i.intValue(),
                              TextOps.safeDelimiter(delimit.value),
                              replace.value);
       }

       /**
        * Replaces the specified 1-based indexed entry in the current instance
        * which must be a delimited list.
        *
        * @param    i
        *           The 1-based index into the list.
        * @param    delimit
        *           The delimiter character.
        * @param    replace
        *           The replacment text.
        *
        * @return   The current instance.  It may have been set to the
        *           <code>unknown value</code> if any of the inputs are
        *           <code>unknown value</code>.
        * @throws ErrorConditionException
        */
       public Text replaceEntry(NumberType i, character delimit, String replace)
       throws ErrorConditionException
       {
          if (i.isUnknown() || delimit.unknown)
          {
             setUnknown();
             return this;
          }

          return replaceEntry(i.intValue(),
                              TextOps.safeDelimiter(delimit.value),
                              replace);
       }

       /**
        * Replaces the specified 1-based indexed comma-delimited entry in the
        * current instance which must be a delimited list.
        *
        * @param    i
        *           The 1-based index into the list.
        * @param    replace
        *           The replacment text.
        *
        * @return   The current instance.  It may have been set to the
        *           <code>unknown value</code> if any of the inputs are
        *           <code>unknown value</code>.
        * @throws ErrorConditionException
        */
       public Text replaceEntry(NumberType i, String replace)
       throws ErrorConditionException
       {
          if (i.isUnknown())
          {
             setUnknown();
             return this;
          }

          return replaceEntry(i.intValue(), ',', replace);
       }

       /**
        * Replaces the specified 1-based indexed entry in the current instance
        * which must be a delimited list.
        *
        * @param    i
        *           The 1-based index into the list.
        * @param    delimit
        *           The delimiter character.
        * @param    replace
        *           The replacment text.
        *
        * @return   The current instance.  It may have been set to the
        *           <code>unknown value</code> if any of the inputs are
        *           <code>unknown value</code>.
        * @throws ErrorConditionException
        */
       public Text replaceEntry(NumberType i, String delimit, Text replace)
       throws ErrorConditionException
       {
          if (i.isUnknown() || replace.unknown)
          {
             setUnknown();
             return this;
          }

          return replaceEntry(i.intValue(),
                              TextOps.safeDelimiter(delimit),
                              replace.value);
       }

       /**
        * Replaces the specified 1-based indexed entry in the current instance
        * which must be a delimited list.
        *
        * @param    i
        *           The 1-based index into the list.
        * @param    delimit
        *           The delimiter character.
        * @param    replace
        *           The replacment text.
        *
        * @return   The current instance.  It may have been set to the
        *           <code>unknown value</code> if any of the inputs are
        *           <code>unknown value</code>.
        * @throws ErrorConditionException
        */
       public Text replaceEntry(NumberType i, String delimit, String replace)
       throws ErrorConditionException
       {
          if (i.isUnknown())
          {
             setUnknown();
             return this;
          }

          return replaceEntry(i.intValue(),
                              TextOps.safeDelimiter(delimit),
                              replace);
       }

       /**
        * Creates a new instance of the same type that represents the
        * <code>unknown value</code>.
        *
        * @return   An instance that represents the <code>unknown value</code>.
        */
       public BaseDataType instantiateUnknown()
       {
          return new character();
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the
        * state of the passed instance. The configuration (case-sensitivity) of
        * this instance is not set.  Use {@link #deepAssign} if both state and
        * configuration must be set.
        *
        * TODO this function can be removed after runtime conversion of dynamic
        * tables containing a clob field is fixed
        *
        * @param    value
        *           The instance from which to copy state. If <code>null</code>
        *           is passed, the instance will be set to the unknown value.
        */
       public void assign(clob value)
       {
          assign((BaseDataType) value);
       }

       @Override
       protected boolean isIncompatibleTypesOnConversion(BaseDataType value)
       {
          // invalid conversion is being thrown even if unknown
          if (value instanceof memptr || value instanceof blob || (value instanceof raw && !value.isUnknown()))
          {
             incompatibleTypesOnConversion();
          }

          return super.isIncompatibleTypesOnConversion(value);
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the state of the passed
        * instance.
        * <p>
        * If the value is not of type <code>Text</code>, the following automatic type conversions
        * will occur:
        * <p>
        * <pre>
        * unknown         - sets the instance to unknown value
        * Text            - assigns normally
        * all other cases - the result of toStringMessage() is assigned
        * </pre>
        * <p>
        * This variant is meant to handle the cases of built-in functions and methods in the 4GL
        * which have polymorphic return types (e.g. DYNAMIC-FUNCTION()). This should NOT be used for
        * non-polymorphic assignments.
        * <p>
        * It is possible that string data could have embedded null characters , but in the 4GL
        * the resulting variable instances are "spacified".  The given value will be processed by
        * {@link #javaSpacifyNull} before storing it in the new instance.
        *
        * @param    value
        *           The instance from which to copy state.
        */
       public void assign(BaseDataType value)
       {
          if (value == null)
          {
             setUnknown();
             return;
          }

          // the value may be a proxy; make sure to unwrap and work with the real value
          value = value.val();
          if (value.isUnknown())
          {
             setUnknown();
          }
          else if (value instanceof Text)
          {
             assign((Text) value);
          }
          else if (value instanceof object)
          {
             assign((object<?>) value);
          }
          else if (value instanceof jobject)
          {
             assign((jobject<?>) value);
          }
          else if (value instanceof rowid)
          {
             incompatibleTypesOnConversion();
          }
          else if (value instanceof BinaryData)
          {
             byte[] bytes = ((BinaryData) value).getByteArray();
             String strVal = new String(bytes, Charset.forName(I18nOps.getJavaCPInternal()));
             assign(javaSpacifyNull(strVal));
             javaSpacifyNull = false;
          }
          else
          {
             String newVal = javaSpacifyNull(value.toStringMessage());
             checkUndoable(() -> new character(newVal));

             // protected against embedded null characters
             this.value = newVal;
             unknown    = false;
             javaSpacifyNull = false;
          }
       }

       /**
        * Convert the specified object instance to number using toString,
        * and assign it to this instance.
        *
        * @param    value
        *           The instance holding a object type.
        */
       public void assign(object<?> value)
       {
          // the value may be a proxy; make sure to unwrap and work with the real value
          if (BaseDataType.isProxy(value))
          {
             assign(value.val());
             return;
          }
          if (value.isUnknown())
          {
             setUnknown();
          }
          else
          {
             assign(value.ref().toLegacyString());
          }
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the state of the passed
        * instance.
        * <p>
        * This form of assignment allows embedded null characters to be assigned from one already
        * existing instance to another if the child class allows it.
        *
        * @param    value
        *           The instance from which to copy state.
        */
       public void assign(Text value)
       {
          // the value may be a proxy; make sure to unwrap and work with the real value
          if (BaseDataType.isProxy(value))
          {
             assign(value.val());
             return;
          }
          String val = value.value;

          // allow values with embedded null characters to be assigned only if the subclass allows
          if (val != null && !isEmbeddedNullAllowed() && val.indexOf('\0') >= 0)
          {
             ErrorManager.recordOrThrowError(142, "Unable to update  Field");
             return;
          }

          checkUndoable(value);

          this.value = val;
          unknown    = value.unknown;
          javaSpacifyNull = value.javaSpacifyNull;
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the
        * state of the passed instance. The configuration (case-sensitivity) of
        * this instance is not set.  Use {@link #deepAssign} if both state and
        * configuration must be set.
        *
        * @param    value
        *           The instance from which to copy state. If <code>null</code>
        *           is passed, the instance will be set to the unknown value.
        */
       public void assign(String value)
       {
          assign(new character(value));
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the state of the passed
        * instance.
        *
        * @param    value
        *           The instance from which to copy state.
        */
       public void assign(Undoable value)
       {
          if (value instanceof Text)
          {
             assign((Text) value);
          }
          else if (value instanceof BaseDataType)
          {
             assign(character.valueOf((BaseDataType) value));
          }
          else
          {
             // should never reach this point, anyway
             assign(value.toString());
          }
       }

       /**
        * Sets the state (data and unknown value) and configuration
        * (case-sensitivity) of this instance based on the state and configuration
        * of the passed instance.
        *
        * @param    value
        *           The instance from which to copy state and configuration.
        */
       public void deepAssign(Text value)
       {
          assign(value);
          caseSens = value.caseSens;
       }

       /**
        * Accesses the state of this instance's <code>String</code> value
        * independently of the state of whether this instance represents the
        * <code>unknown value</code>.  If this instance represents the
        * <code>unknown value</code>, the returned <code>String</code> is
        * <b>not valid</b>!
        *
        * @return   The state of this instance's <code>String</code> value.
        */
       public String getValue()
       {
          return value;
       }

       /**
        * Sets the state of this instance's <code>String</code> value and sets the
        * <code>unknown</code> state for this instance to <code>false</code>.
        * <p>
        * Strings data can have embedded null characters, but in the 4GL the resulting variable
        * instances are "spacified".  The given value will be processed by {@link #javaSpacifyNull}
        * before storing it in the new instance.
        *
        * @param    value
        *           The state to set this instance's <code>String</code> value.
        */
       public void setValue(String value)
       {
          String val = javaSpacifyNull(value);

          checkUndoable(() -> new character(val));

          // protected against embedded null characters
          this.value = val;
          unknown    = false;
          javaSpacifyNull = false;
       }

       /**
        * Determines if this instance compares case sensitively.
        *
        * @return   <code>true</code> if this instance is set to case sensitive.
        */
       public boolean isCaseSensitive()
       {
          return caseSens;
       }

       /**
        * Sets the state of this instance's compares case sensitive flag.
        * <p>
        * This method is used to set the current value in a manner that does not
        * schedule an automated reset to the prior value later. Thus, this method
        * is suitable for the initial configuration of a new instance.  If a
        * temporary case-sensitivity change is needed, use
        * {@link #setTemporaryCaseSensitive}.
        *
        * @param    caseSens
        *           <code>true</code> if this instance must be set to case
        *           sensitive.
        */
       public void setCaseSensitive(boolean caseSens)
       {
          if (caseSens != this.caseSens)
          {
             checkUndoable(true);
          }

          this.caseSens = caseSens;
       }

       /**
        * Sets the state of this instance's compares case sensitive flag for the
        * duration of the caller's current scope.  At the end of the caller's
        * scope a notification will trigger a reset of the case sensitive flag
        * to the current value.
        * <p>
        * This method should only be used when importing references via the
        * {@link SharedVariableManager} as a local instance or a new shared
        * instance would not need to be reset, since they would have no prior
        * value to which to return.  To set the initial case-sensitivity, please
        * se {@link #setCaseSensitive}.
        * <p>
        * The {@link TransactionManager#registerFinalizable} facility is used to
        * obtain the callback necessary to reset the sensitivity at end of the
        * caller's scope.
        *
        * @param    caseSens
        *           <code>true</code> if this instance must be set to case
        *           sensitive.
        */
       public void setTemporaryCaseSensitive(boolean caseSens)
       {
          // do nothing if the request precision is the same as the current
          // precision
          if (this.caseSens != caseSens)
          {
             // remember the old value
             boolean old = this.caseSens;

             // reset
             setCaseSensitive(caseSens);

             // schedule the reset
             SensitivityResetter sr = new SensitivityResetter(old);
             TransactionManager.registerFinalizable(sr, false);
          }
       }

       /**
        * Test equality between the internal values of this object and the given parameter on a case
        * sensitive basis. Before the internal values are checked, the unknown states of both operands
        * are compared. If both are unknown, {@code true} is returned. If only one is unknown,
        * {@code false} is returned. Otherwise, the case sensitive test is performed.
        *
        * @param   c
        *          Another character instance to test for case sensitive equality with this object.
        *
        * @return  {@code true} if this object and the given object test as equal, else {@code false}.
        */
       public boolean equalsCaseSensitively(Text c)
       {
          boolean u1 = isUnknown();
          boolean u2 = c.isUnknown();

          if (u1 && u2)
          {
             return true;
          }

          if (u1 || u2)
          {
             return false;
          }

          // quick out if the values are the same object instance
          if (value == c.value)
          {
             return true;
          }

          // prepare the data
          String s1 = StringHelper.safeTrimTrailingSpaces(value);
          String s2 = StringHelper.safeTrimTrailingSpaces(c.value);

          return s1.equals(s2);
       }

       /**
        * Hash code implementation which is consistent with {@link #equalsCaseSensitively}.
        *
        * @return  Hash code value for this object instance.
        */
       public int caseSensitiveHashCode()
       {
          int result = 17;
          if (isUnknown())
          {
             return result;
          }

          result = 37 * result + value.hashCode();

          return result;
       }

       /**
        * Determines if this instance represents the <code>unknown value</code>.
        *
        * @return   <code>true</code> if this instance is set to the
        *           <code>unknown value</code>.
        */
       public boolean isUnknown()
       {
          return unknown;
       }

       /**
        * Sets the state of this instance's <code>unknown value</code> flag to
        * <code>true</code>.
        * <p>
        * <b>Warning: a separate call is needed to ensure that the data of this
        * instance is set to the correct value.</b>
        */
       public void setUnknown()
       {
          if (!unknown)
          {
             checkUndoable(true);
          }

          unknown = true;
          value = null;
          javaSpacifyNull = true;
       }

       /**
        * Replacement for the default object reading method. The latest
        * state is read from the input source.
        *
        * @param    in
        *           The input source from which fields will be restored.
        *
        * @throws   IOException
        *           In case of I/O errors.
        * @throws   ClassNotFoundException
        *           If payload can't be instantiated.
        */
       public void readExternal(ObjectInput in)
       throws IOException,
              ClassNotFoundException
       {
          byte flags = in.readByte();
          unknown = (flags & 1) != 0;
          caseSens = (flags & 2) != 0;
          if ((flags & 4) != 0)
          {
             value = readString(in);
          }
       }

       /**
        * Replacement for the default object writing method. The latest
        * state is written to the output destination.
        *
        * @param    out
        *           The output destination to which fields will be saved.
        *
        * @throws   IOException
        *           In case of I/O errors.
        */
       public void writeExternal(ObjectOutput out)
       throws IOException
       {
          int flags = unknown ? 1 : 0;
          if (caseSens)
          {
             flags |= 2;
          }

          // the next object can be null so we write an indicator var first to
          // notify reader if there is following data to be read

          boolean there = (value != null);
          if (there)
          {
             flags |= 4;
          }
          out.writeByte(flags);

          if (there)
          {
             writeString(out, (String)value);
          }
       }

       /**
        * Compares this instance with the specified instance and returns a -1 if this instance is less
        * than the specified, 0 if the two instances are equal and 1 if this instance is greater than
        * the specified instance. This is the implementation of the {@code Comparable} interface.
        * <p>
        * Core worker that handles comparisons between 2 string operands after removing trailing
        * spaces and honoring the case sensitivity operands.
        * <p>
        * Case-insensitive comparisons are obtained by manually uppercasing both operands which is how
        * Progress does it. Lowercase comparisons cannot be used because the results for
        * {@link CompareOps#isGreaterThan}, {@link CompareOps#isLessThan},
        * {@link CompareOps#isGreaterThanOrEqual} and {@link CompareOps#isLessThanOrEqual} would be
        * different in Java than in Progress when the following characters are included in the string:
        * <pre>
        * [
        * \
        * ^
        * _
        * '
        * </pre>
        * <p>
        * The algorithm will fail to give meaningful results in the case where one tries to sort
        * against other objects that do not represent compatible values.
        *
        * @param   obj
        *          The instance to compare against.
        *
        * @return  -1, 0 or 1 depending on if this instance is less than, equal to or greater
        *          (respectively) than the specified instance {@code obj}.
        */
       public int compareTo(Object obj)
       {
          if (unknown)
          {
             return -1;
          }

          Text c = null;

          if (obj instanceof Text)
          {
             c = (Text) obj;

             // quick out if the String objects are the same instance
             if ((unknown && c.unknown) || value == c.value)
             {
                return 0;
             }
             else if (c.unknown)
             {
                return -1;
             }
          }
          else if (obj instanceof String)
          {
             // quick out if the String objects are the same instance
             if (value == obj)
             {
                return 0;
             }

             c = new character((String) obj);
          }
          else
          {
             return -1;
          }

          // prepare the data
          String s1 = StringHelper.safeTrimTrailingSpaces(value);
          String s2 = StringHelper.safeTrimTrailingSpaces(c.value);

          // if either instance requires case sensitivity, force a case sensitive comparison
          if (caseSens || c.caseSens)
          {
             return s1.compareTo(s2);
          }

          // DO NOT use String.compareToIgnoreCase() since this lowercases and yields different
          // results for >, <, >= and <= forms when [ \ ^ _ ' characters are included in the operands
          if (s1.isEmpty() && s2.isEmpty())
          {
             return 0;
          }
          else if (s1.isEmpty())
          {
             return -1;
          }
          else if (s2.isEmpty())
          {
             return 1;
          }

          return TextOps.getUpper(s1).compareTo(TextOps.getUpper(s2));
       }

       /**
        * Hash code implementation which is consistent with {@link
        * BaseDataType#equals}.
        *
        * @return  Hash code value for this object instance.
        */
       public int hashCode()
       {
          int result = 17;
          if (isUnknown())
          {
             return result;
          }

          String s = StringHelper.safeTrimTrailing(value);
          result = 37 * result + (caseSens ? s.hashCode() : StringHelper.hashCodeCaseInsensitive(s));

          return result;
       }

       /**
        * Return the current value as a Java wrapper.
        *
        * @return  Current value or <code>null</code> if unknown.
        */
       public String toJavaType()
       {
          return (isUnknown() ? null : getValue());
       }

       /**
        * Get the state of the {@link #javaSpacifyNull} flag.
        *
        * @return   See above.
        */
       public boolean isJavaSpacifyNull()
       {
          return javaSpacifyNull;
       }

       /**
        * Returns the length of the current Text object instance in chars.  Internal version.
        *
        * @return  Current text variable char length.
        */
       protected int lengthInt()
       {
          return value.length();
       }

       /**
        * Check the overlay type description and throw an exception if the type
        * is not yet supported. The check is case-insensitive.
        *
        * @param    type
        *           Must be "character" at this time.
        *
        * @throws   IllegalArgumentException
        *           If the type doesn't match "character".
        */
       private void checkOverlayType(String type)
       {
          if (type != null && !type.equalsIgnoreCase("character"))
          {
             String err = String.format("OVERLAY type %s is not supported!", type);
             throw new IllegalArgumentException(err);
          }
       }

       /**
        * Core method to implement overlay and replace support. This method
        * changes the current value of this instance, overwriting characters
        * at the specified index given by <code>pos</code> with the text
        * specified by <code>text</code>.  The number of replaced characters
        * is specified by <code>len</code>, with a negative number indicating
        * that the length of characters in <code>text</code> is used.  If
        * <code>overlay</code> mode is requested, then the <code>len</code>
        * parameter also limits the number of characters in the <code>text</code>
        * parameter which will be used as the replacement.  If any of the
        * parameters are <code>unknown value</code>, the instance will be set
        * to the <code>unknown value</code>.
        *
        * @param    text
        *           Replacement text.
        * @param    pos
        *           The 1-based index into the string at which the replacement
        *           is to occur.
        * @param    len
        *           The number of characters in the source string to replace.
        * @param    overlay
        *           <code>true</code> if overlay mode is required.
        */
       private void replaceWorker(Text       text,
                                  NumberType pos,
                                  NumberType len,
                                  boolean    overlay)
       throws ErrorConditionException
       {
          // quick out
          if (this.isUnknown())
          {
             // nothing to do, you can't replace chars in the unknown value
             return;
          }

          // are any of our inputs unknown?
          if (text.isUnknown() || pos.isUnknown() || len.isUnknown())
          {
             // set our result to unknown and return
             setUnknown();
             return;
          }

          String rtxt = text.getValue();
          int    rpos = pos.intValue();
          int    rlen = len.intValue();

          // argument check
          if (rpos < 1)
          {
             TextOps.genNegativeIndexError();
             return;
          }

          // convert from 1-based to 0-based index
          rpos--;

          // -1 is equivalent of the length of the replacement text
          if (rlen == -1)
          {
             rlen = rtxt.length();
          }

          // replacement text is exactly as passed-in if not in overlay mode
          if (overlay)
          {
             // overlay mode means that the len parameter also changes the
             // replacement text
             if (rtxt.length() < rlen)
             {
                // pad to the requested length
                rtxt = StringHelper.leftAlignText(rtxt, rlen);
             }
             else
             {
                // truncate the replacement text
                rtxt = rtxt.substring(0, rlen);
             }
          }

          StringBuilder sb = new StringBuilder();

          // start with the beginning of the string
          if (rpos >= value.length())
          {
             // pad to the requested position
             sb.append(StringHelper.leftAlignText(value, rpos));
          }
          else
          {
             // just substring the first portion
             sb.append(value.substring(0, rpos));
          }

          // insert our replacement text
          sb.append(rtxt);

          // index of the end of the string
          int elen = rpos + rlen;

          // if there is any text at the end of the string, add that
          if (value.length() > elen)
          {
             sb.append(value.substring(elen));
          }

          String newVal = sb.toString();

          checkUndoable(() -> new character(newVal));

          // save the results into our instance data; does not need null character protection
          // since the data is already coming from two existing text instances of the same type
          value = newVal;
       }

       /**
        * Helper to reset the associated instance's case sensitivity to a prior
        * value based on a notification that a particular scope has exited.  This
        * scope notification support is provided by
        * {@link TransactionManager#registerFinalizable} and the {@link
        * Finalizable} interface.
        */
       private class SensitivityResetter
       implements Finalizable
       {
          /** The sensitivity to which the containing instance will be reset. */
          boolean oldCaseSens;

          /**
           * Construct an instance which can reset the containing instance's
           * case sensitivity when a notification is received.  See
           * {@link character#setTemporaryCaseSensitive}.
           *
           * @param    oldCaseSens
           *           The case sensitivity to which the containing instance must
           *           be reset.
           */
          SensitivityResetter(boolean oldCaseSens)
          {
             this.oldCaseSens = oldCaseSens;
          }

          /**
           * Processes the end of scope notification and resets the associated
           * instance's case sensitivity.  This notification is provided by the
           * {@link TransactionManager}.
           */
          public void finished()
          {
             Text.this.caseSens = oldCaseSens;
          }

          /**
           * Provides a notification that the external program scope in which the object is registered is
           * is being deleted and the object's reference will be lost after this method is called.
           * <p>
           * This is a no-op in this case.
           */
          @Override
          public void deleted()
          {
             // no-op
          }

          /**
           * Processes the iterate notification provided by the {@link
           * TransactionManager}.  This implementation does nothing.
           */
          public void iterate()
          {
          }

          /**
           * Processes the retry notification provided by the {@link
           * TransactionManager}.  This implementation does nothing.
           */
          public void retry()
          {
          }
       }
    }