# longchar.java

    /*
    ** Module   : longchar.java
    ** Abstract : Progress 4GL compatible longchar object
    **
    ** Copyright (c) 2012-2025, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
    ** 001 AIL 20121206 Created initial version to support for Progress LONGCHAR type.
    ** 002 GES 20130322 Added the BDT c'tor.
    ** 003 VIG 20131003 Overridden instantiateUnknown method to return longchar
    ** 004 MAG 20140602 Fixed the BDT c'tor and modified the assign(BDT,boolean) on _POLY morphing.
    **                  Override compareTo() method for _POLY morphing.
    ** 005 GES 20141003 Added overridden method to disable assignments of values with embedded
    **                  null characters.
    ** 006 CA  20151222 Added instantiateDefaultExtent.
    ** 007 GES 20171207 Removed forced version of assign().
    ** 008 ECF 20181214 Implemented LargeObject interface.
    **     CA  20181221 Added GET-CODEPAGE variants.
    ** 009 EVL 20190412 Added runtime support for GET-CODEPAGE, FIX-CODEPAGE, IS-CODEPAGE-FIXED.
    ** 010 ECF 20190628 Implemented COPY-LOB runtime.
    ** 011 HC  20190805 Added method instantiateDefault.
    ** 012 CA  20191119 Fixed write(...) - if the data is empty, then assign it.
    ** 013 CA  20191203 Fixed overlay and unknown COPY-LOB related bugs.
    ** 014 ME  20200401 Fixed codepage set on assign, fix codepage validation.
    ** 015 ME  20200810 Cleanup for toStringExport().
    **     IAS 20201007 Added Type enum
    **     ME  20201009 Allow codepage to be reset back to unknown in fix-codepage.
    **     OM  20210203 Added API to accept unknown parameter for fixCodePage() method.
    **     OM  20210312 FIX-CODE-PAGE statement is converted to a static method to accomodate the cases when
    **                  the object is obtained by dereference. Improved COPY-LOB engine and validations.
    **     OM  20210404 Reworked codepage management for CLOB fields.
    **     CA  20210428 Fixed conversion when the lvalue for an assign-styme stmt is a OO property (chained or
    **                  not). In this case, a setter must wrap the assign-style stmt, to change the property's
    **                  value.
    **     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.
    **     CA  20210713 Fixed COPY-LOB when source is unknown (must set the longchar target to unknown).
    **     AL2 20220328 Added proxy checks for BDT.
    **     EVL 20220406 Fix for pverlay mode in write method when we need to replace the text considering offset
    **                  in target object.
    **     EVL 20220411 User regular overlay method when using offset with overlay mode.  Adding trim option
    **                  support for writing to longchar.  Reworked overlay trim because of different meaning
    **                  the word 'trim' has here.
    **     CA  20221024 'asByteArray' must use the actual codepage set at the longchar instance, regardless if
    **                  this was set via FIX-CODEPAGE or via an assignment from another longchar instance.
    ** 016 HC  20230118 Eliminated some of the uses of String.toUpperCase and/or String.toLowerCase
    **                  for performance.
    ** 017 CA  20230215 'duplicate()' method returns the real type instead of BDT.
    ** 018 EVL 20230317 Adding supprt for longchar variable length override in some conditions.  This value can
    **                  differ from backend String value length.
    **     EVL 20230322 Changed approach for setting length override value.  If was already set the new value
    **                  accepted if it exceeds the current one.
    ** 019 CA  20230526 Added 'forceCharacterType', emitted for a 'ELSE' longchar expr in a ternary IF which is
    **                  used for a builtin function call.
    ** 020 EVL 20240607 Fix for unwanted message for assignment to unknown value. If codepage was already fixed
    **                  no need to do this again.
    ** 021 SP  20240805 Fix for unwanted 11683 error for assignment to unknown value. Moved the fixCodepage call
    **                  after the assignment.
    ** 022 CA  20241030 Avoid 'Text.javaSpacifyNull' if the string is known to already have been processed.
    ** 023 AP  20250318 Improved toStringMessage method to take into account unknown values.
    ** 024 TG  20250519 Removed longchar.getcodePage(BaseDataType) because it is implemented in I18nOps
    **         20250520 Replaced new character() with character.UNKNOWN in getCodePage() for better performance
    */

    /*
    ** 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 com.goldencode.util.*;
    import java.io.*;

    /**
     * A class that represents a Progress 4GL compatible longchar object whose data is mutable.
     * <p>
     * Although Progress documentation states that the default value of any <code>longchar</code>
     * variable is the unknown value, the real behavior is that it gets initialized to the empty
     * string, same as character variables get initialized.
     */
    public class longchar
    extends Text
    implements LargeObject
    {
       /** Variable to store code page value set by FIX-CODEPAGE statement. */
       private String codePage;

       /**
        * Variable to store if code page value was set by FIX-CODEPAGE statement.
        * IS-CODEPAGE-FIXED will relay on this
        * */
       private boolean fixedCodepage;

       /**
        * The override value for variable length.  Can be changed in copy-lob operration if target is longchar
        * and source is memptr.
        */
       private int overrideLength = -1;

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

       /**
        * 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 (this is delegated to the superclass' constructor).
        * <p>
        * Only Text (character or longchar) BDT's can be converted to longchar.
        *
        * @param    value
        *           The value to be used for this instance.
        */
       public longchar(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
          {
             if (value instanceof Text)
             {
                assign(value);
             }
             else
             {
                incompatibleTypesOnConversion();
             }
          }
       }

       /**
        * 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>.
        * If value is not "" then codepage is cpinternal
        *
        * @param    value
        *           The character value of the resulting instance.
        */
       public longchar(String value)
       {
          super(value);

          if (!TextOps.isEmpty(value))
          {
             codePage = I18nOps._getCPInternal();
          }
       }

       /**
        * 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>.
        *
        * @param    value
        *           The character value of the resulting instance.
        * @param    caseSens
        *           Specifies whether this instance should use case sensitive
        *           comparisons.
        */
       public longchar(String value, boolean caseSens)
       {
          super(value, caseSens);

          if (!TextOps.isEmpty(value))
          {
             codePage = I18nOps._getCPInternal();
          }
       }

       /**
        * Constructs an instance that has a <code>char</code> value passed
        * in and which <b>does not</b> represent the <code>unknown value</code>.
        *
        * @param    ch
        *           The character value of the resulting instance.
        */
       public longchar(char ch)
       {
          super(ch);
       }

       /**
        * Create a new instance, by copying the state of the passed {@link Text}.
        *
        * @param    value
        *           The value which needs to be copied.
        */
       public longchar(Text value)
       {
          super(value);
       }

       /**
        * Force the longchar value as a character type, without limit length enforcement.
        *
        * @param    val
        *           The value to convert.
        *
        * @return   The new character value, with {@link character#forceLengthLimit()} disabled.
        */
       public static character forceCharacterType(longchar val)
       {
          return new character(val)
          {
             @Override
             protected boolean forceLengthLimit()
             {
                return false;
             }
          };
       }

       /**
        * Get the type
        * @return type
        */
       public Type getType()
       {
          return Type.LONGCHAR;
       }

       /**
        * Report if existing instances that have embedded null characters are allowed to be
        * assigned to this instance.
        *
        * @return   Always <code>false</code>.
        */
       public boolean isEmbeddedNullAllowed()
       {
          return false;
       }

       /**
        * 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</code>
        * interface.
        *
        * @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</code>.
        */
       public int compareTo(Object obj)
       {
          if (obj instanceof BaseDataType)
          {
             return super.compareTo(new longchar((BaseDataType) obj));
          }

          return super.compareTo(obj);
       }

       /**
        * Returns the code page of a the specified longchar variable. If the
        * specified LONGCHAR is empty and the code page was not fixed using the
        * FIX-CODEPAGE statement, must return the unknown value.
        *
        * @param    value
        *           The value for which the codepage will be retrieved.
        *
        * @return   See above
        */
       public static character getCodePage(longchar value)
       {
          return value.getCodePage();
       }

       /**
        * Returns the code page of a the current longchar variable. If the LONGCHAR is unknown or empty and the
        * code page was not fixed using the FIX-CODEPAGE statement, the unknown value is returned.
        *
        * @return   See above
        */
       public character getCodePage()
       {
          String cp = _getCodePage();
          return (cp == null) ? character.UNKNOWN : new character(cp.toUpperCase());
       }

       /**
        * Returns the code page of a the current longchar variable. If the LONGCHAR is unknown or empty and the
        * code page was not fixed using the FIX-CODEPAGE statement, the unknown value is returned.
        * <p>
        * This is the String returning value, the unknown value is represented by {@code null}.
        *
        * @return   See above
        */
       public String _getCodePage()
       {
          if (fixedCodepage)
          {
             return codePage;
          }

          if (isUnknown() || value.isEmpty())
          {
             return null;
          }

          // at this moment codePage must not be null
          return codePage;
       }

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

       /**
        * Return {@code true} if the codepage of the given longchar variable is fixed.
        *
        * @param    value
        *           The value for which the codepage will be retrieved.
        *
        * @return   See above.
        */
       public static logical isCodePageFixed(longchar value)
       {
          return new logical(value != null && value.isCodePageFixed());
       }

       /**
        * Implements Progress 4GL feature for FIX-CODEPAGE statement.
        *
        * @param   longcharRef
        *          A reference to a {@code longchar} variable.
        * @param   cpToUse
        *          The new value for codepage to be fixed.
        *
        * @return  The longchar reference.
        */
       public static longchar fixCodePage(longchar longcharRef, String cpToUse)
       {
          return fixCodePage(longcharRef, cpToUse, false);
       }

       /**
        * Implements Progress 4GL feature for FIX-CODEPAGE statement.
        * <p>
        * Note: actually this method should be as less visible as possible, preferably private. However, it must
        *       be accessed from {@link com.goldencode.p2j.persist.FieldReference}.
        *
        * @param   longcharRef
        *          A reference to a {@code longchar} variable.
        * @param   cpToUse
        *          The new value for codepage to be fixed. If {@code prepareClob} is true, this parameter must NOT
        *          be {@code null}.
        * @param   prepareClob
        *          Allow to set a FIXED the CODEPAGE on target {@code longcharRef} only if it is a field (hence
        *          a {@code clob}) only when this parameter is {@code true}. Otherwise the procedure is finished.
        *          At least this is behaviour in 4GL: ERROR-STATUS:ERROR = YES ERROR-STATUS:GET-NUMBER(1) = 0.
        *          ERROR-STATUS:get-message(1) = "".
        *
        * @return  The longchar reference.
        */
       public static longchar fixCodePage(longchar longcharRef, String cpToUse, boolean prepareClob)
       {
          if (longcharRef instanceof clob)
          {
             if (!prepareClob || longcharRef.fixedCodepage)
             {
                // yes!, isn't it funny when procedure stops without any apparent reason?
                ErrorManager.recordOrThrowError(0, "", false);
                return longcharRef;
             }
          }

          // allow to lazily set the CP on CLOB fields
          if (!TextOps.isEmpty(longcharRef) && !prepareClob)
          {
             ErrorManager.recordOrThrowError(11683);
             // FIX-CODEPAGE can only be run when the LONGCHAR variable is empty.
          }
          else
          {
             if (cpToUse == null)
             {
                longcharRef.codePage = null;
                longcharRef.fixedCodepage = false;
             }
             else if (!I18nOps.isSupported(cpToUse))
             {
                ErrorManager.recordOrThrowError(912, cpToUse.toUpperCase(), I18nOps.CONVERSION_MAP_FILE);
                // Code page attribute table for <code-page> was not found in <filename>.
             }
             else
             {
                if (I18nOps.CP_UNDEFINED.equalsIgnoreCase(cpToUse))
                {
                   ErrorManager.recordOrThrowError(12125, cpToUse, "");
                   // The codepage "<codepage>" cannot be used for CLOB or LONGCHAR variables.
                }
                else
                {
                   longcharRef.codePage = cpToUse;
                   longcharRef.fixedCodepage = true;
                }
             }
          }

          return longcharRef;
       }

       /**
        * Implements Progress 4GL feature for FIX-CODEPAGE statement.
        *
        * @param   longcharRef
        *          A reference to a {@code longchar} variable.
        * @param   cpToUse
        *          The new value for codepage to be fixed.
        */
       public static void fixCodePage(longchar longcharRef, Text cpToUse)
       {
          fixCodePage(longcharRef, (cpToUse == null) ? null : cpToUse.toJavaType());
       }

       /**
        * Implements Progress 4GL feature for FIX-CODEPAGE statement. This variant is used when the first
        * argument is obtained by dereferenciation.
        *
        * @param   value
        *          A reference to a BDT, obtained by dereferenciation.
        * @param   cpToUse
        *          The new value for codepage to be fixed.
        */
       public static void fixCodePage(BaseDataType value, String cpToUse)
       {
          if (value instanceof longchar)
          {
              fixCodePage((longchar) value, cpToUse);
          }
          else
          {
             ErrorManager.recordOrThrowError(5729);
             // Incompatible datatypes found during runtime conversion. (5729)
          }
       }

       /**
        * Implements Progress 4GL feature for FIX-CODEPAGE statement. This variant is used when either of the
        * arguments or both are obtained by dereferenciation.
        *
        * @param   value
        *          A reference to a BDT, obtained by dereferenciation.
        * @param   cpToUse
        *          The new value for codepage to be fixed.
        */
       public static void fixCodePage(BaseDataType value, BaseDataType cpToUse)
       {
          if (cpToUse == null || cpToUse.isUnknown())
          {
             fixCodePage(value, (String) null);
          }
          else if (cpToUse instanceof Text && !(cpToUse instanceof clob))
          {
             fixCodePage(value, ((Text) cpToUse).toJavaType());
          }
          else if (cpToUse instanceof blob || cpToUse instanceof clob ||
                   cpToUse instanceof raw  || cpToUse instanceof memptr)
          {
             ErrorManager.recordOrThrowError(5729);
             // Incompatible datatypes found during runtime conversion. (5729)
          }
          else
          {
             ErrorManager.recordOrThrowError(912, cpToUse.toStringMessage(), I18nOps.CONVERSION_MAP_FILE);
             // Code page attribute table for <code-page> was not found in <filename>. (912)
          }
       }

       /**
        * Implements Progress 4GL feature for FIX-CODEPAGE statement.
        *
        * @param    cpToUse
        *           The new value for codepage to be fixed.
        */
       public void fixCodePage(String cpToUse)
       {
          fixCodePage(this, cpToUse);
       }

       /**
        * Implements Progress 4GL feature for FIX-CODEPAGE statement.
        *
        * @param    cpToUse
        *           The new value for codepage to be fixed.
        */
       public void fixCodePage(character cpToUse)
       {
          fixCodePage(this, (cpToUse != null) ? cpToUse.getValue() : null);
       }

       /**
        * Return the value codepage fixed status.
        *
        * @return   <code>TRUE</code> is codepage was fixed for this variable,
        *           <code>FALSE</code> otherwise.
        */
       public boolean isCodePageFixed()
       {
          return fixedCodepage;
       }

       /**
        * Return the default display format string for this type.
        *
        * @return   The default format string.
        */
       public String defaultFormatString()
       {
          return null;
       }

       /**
        * Does the same as standard <code>clone()</code> method but returns an
        * instance of <code>BaseDataType</code> and doesn't throw the
        * <code>CloneNotSupportedException</code>.
        *
        * @return   A clone of this instance.
        */
       public longchar duplicate()
       {
          return new longchar(this);
       }

       /**
        * Creates a string representation of the instance data in a form that is compatible with the
        * {@code MESSAGE} language statement.  If the instance represents the {@code unknown value}, a '?' will
        * be returned.
        *
        * @return   The 'message' formatted string.
        */
       public String toStringMessage()
       {
          return toStringExport();
       }

       /**
        * Creates a string representation of the instance data using the 'export'
        * format.  For the character data type the entire string is returned
        * enclosed in a pair of double quotes.  If the instance represents the
        * <code>unknown value</code>, a '?' will be returned.  Any instances of
        * double quotes inside the string will be doubled up on output.
        *
        * @return   The 'export' formatted string.
        */
       public String toStringExport()
       {
          return isUnknown() ? "?" : getValue();
       }

       /**
        * Creates a string representation of the instance data using the user- specified Progress 4GL format
        * string. The algorithm to format a {@code longchar} is basically the same as for any {@code character}.
        *
        * @param    fmt
        *           The Progress 4GL format string for a character data type.
        *
        * @return   The formatted string.
        */
       public String toString(String fmt)
       {
          if (isUnknown())
          {
             return "?";
          }

          String clean = null;

          // implement 'default' behavior
          if (fmt == null)
          {
             fmt = character.defaultFormat;

             // default case doesn't specify
             clean = value;
          }
          else
          {
             // for some reason, when there is an explicit format string, the value is specified
             clean = javaSpacifyNull ? javaSpacifyNull(value) : value;
          }

          // make the format string easier to process
          String format = character.preprocessFormatString(fmt);

          // trim trailing whitespace off the value
          String trimmed = StringHelper.safeTrimTrailing(clean);

          // build the output string and return it
          return character.processDataString(trimmed, format);
       }

       /**
        * Indicate whether this object manages character data (as opposed to binary data).
        *
        * @return  Always {@code true}.
        */
       @Override
       public boolean isCharacterData()
       {
          return true;
       }

       /**
        * The length of the data managed by this object.
        *
        * @return  The length, or 0 if uninitialized/unknown.
        */
       @Override
       public long lengthOf()
       {
          if (isUnknown())
          {
             return 0;
          }

          return value.length();
       }

       /**
        * Return the entire contents of the current buffer as an array. This may be a copy of the data, depending
        * on the subclass' implementation. The byte array is the binary representation of this LOB using the
        * configured codepage. If none was configured the default CPInternal is used.
        *
        * @param   pos
        *          Starting offset position.
        * @param   len
        *          Length of range to return.
        *
        * @return  The contents of the instance. If the instance is uninitialized the returned
        *          value will be a 0 length array. If the instance is {@code unknown} or has an
        *          undefined size, then {@code null} will be returned.
        */
       @Override
       public byte[] asByteArray(long pos, long len)
       {
          if (isUnknown())
          {
             return null;
          }

          int offset = (int) Math.max(0, pos);
          int length = (int) Math.min(len, value.length());
          String s = (pos == 0 && len == length) ? value : value.substring(offset, length);

          String codePage = this.codePage != null ? this.codePage : I18nOps._getCPInternal();
          String charset = I18nOps.convmap2Java.get(codePage);

          try
          {
             return s.getBytes(charset);
          }
          catch (UnsupportedEncodingException e)
          {
             ErrorManager.recordOrThrowError(912, codePage, I18nOps.CONVERSION_MAP_FILE);
             // Code page attribute table for <code-page> was not found in <filename>.
             return null;
          }
       }

       /**
        * Write the given character data into the data managed by this object, overwriting the data
        * that are at the given location, if any.
        * <p>
        * This implementation:
        * <ul>
        * <li>ignores the {@code trim} option (i.e., any existing characters beyond the end of the
        *     write are left in place, regardless of the value of {@code trim});</li>
        * <li>extends the target size if the write goes past the end of the existing value.</li>
        * </ul>
        *
        * @param   overlay
        *          Flag indicating if the OVERLAY option is used.
        * @param   data
        *          Data to be assigned to this object.
        * @param   offset
        *          Offset position (in characters) in the target large object.
        * @param   trim
        *          {@code true} to truncate any remaining data in the target large object; {@code
        *          false} to leave remaining data alone. Ignored.
        */
       @Override
       public void write(boolean overlay, String data, int offset, boolean trim)
       {
          // if we are in overlay mode, fill with 'offset - 1' spaces to the right
          // TODO: if value is null and source is clob, then this remains unknown....
          if (overlay && (value == null || offset >= value.length()))
          {
             int rightFill = offset - (value == null ? 0 : value.length());
             assign(value + StringHelper.rightAlignText("", rightFill));
          }

          int dataLen;

          if (data == null || (dataLen = data.length()) == 0 || (offset == 0 && data.equals(value)))
          {
             // nothing to write or data already is stored in this object (we don't do a deep equals
             // check because it could be expensive and the common case is likely a full assignment
             // anyway, so don't take the hit here, just quickly check for object reference equality)
             if (overlay)
             {
                // do the trimming is requested
                if (trim && value != null && offset >= 0 && offset < value.length())
                {
                   // do not using regular trim function here
                   assign(value.substring(0, offset) + ((data != null) ? data : ""));
                }
             }
             else
             {
                if (data != null)
                {
                   assign(data);
                }
                else
                {
                   setUnknown();
                   // TODO: CA - this was commented out because 'COPY-LOB tt1.clobField to lc' sets the target
                   // to unknown if tt1.clobField is unknown.
                   // assign("");
                }
             }

             return;
          }

          int currentLen = (value != null) ? value.length() : 0;

          if (offset > currentLen)
          {
             ErrorManager.recordOrThrowError(11316);
             // Could not obtain LONGCHAR. (11316)
             return;
          }

          // simple case: just replace existing value with data
          if (!overlay || isUnknown() || (offset == 0 && (currentLen <= dataLen)))
          {
             assign(data);
             return;
          }

          // complex case: overlay data over existing data, possibly extending the length
          int endData = offset + dataLen;
          int newLen = trim ? endData : Math.max(currentLen, endData);
          char[] buf = new char[newLen];

          if (offset > 0)
          {
             // copy first portion from existing value into new buffer
             value.getChars(0, offset, buf, 0);
          }

          // copy new data to its target offset in new buffer
          data.getChars(0, dataLen, buf, offset);

          if (newLen > endData)
          {
             // copy last portion from existing value into new buffer
             value.getChars(endData, currentLen, buf, endData);
          }

          assign(new String(buf));
       }

       /**
        * Creates a new instance of the same type that represents the
        * default initialized value.
        *
        * @return   An instance that represents the default value.
        */
       public BaseDataType instantiateDefault()
       {
          return new longchar("");
       }

       /**
        * Get the default initialization for an extent variable of this type.
        *
        * @return   See above.
        */
       @Override
       protected BaseDataType instantiateDefaultExtent()
       {
          return new longchar("");
       }

       /**
        * Assigns the value represented by {@code value} to this object. If this {@code longchar} does not have a
        * fixed codepage, the default codepage (SESSION:CPINTERNAL) is used. Otherwise a codepage conversion
        * occurs.
        *
        * @param   value
        *          The source {@code Text} to be assigned.
        */
       @Override
       protected boolean isIncompatibleTypesOnConversion(BaseDataType value)
       {
          // invalid conversion is being thrown even if unknown
          if (value instanceof blob ||
          // if not unknown only longchar/character allowed
                   (value != null && !value.isUnknown() && !(value instanceof longchar)
                            && !(value instanceof character)))
          {
             incompatibleTypesOnConversion();
          }

          return super.isIncompatibleTypesOnConversion(value);
       }

       /**
        * Sets new value for longchar overridden length.  Can be more than backend String variable and sets up
        * in copy-lob operation in some conditions.
        *
        * @param    newOverrideLength
        *           The new value of the internal longchar length.
        */
       protected void setOverrideLength(int newOverrideLength)
       {
          if(newOverrideLength == -1 || newOverrideLength > overrideLength)
          {
             overrideLength = newOverrideLength;
          }
       }

       /**
        * Returns the length of the current Text object instance in chars.  Internal version.
        *
        * @return  Current text variable char length.
        */
       @Override
       protected int lengthInt()
       {
          return overrideLength != -1 ? overrideLength : super.lengthInt();
       }

       @Override
       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;
          }
          if (value instanceof longchar)
          {
             assign((longchar) value);
          }
          else
          {
             if (value.isUnknown())
             {
                // the fixed codepage is kept
                setUnknown();
             }
             else
             {
                if (!isCodePageFixed() && !TextOps.isEmpty(value))
                {
                   codePage = I18nOps._getCPInternal();
                }

                super.assign(value);
             }
          }
       }

       /**
        * Assigns the value represented by {@code value} to this object. If this {@code longchar} does not have a
        * fixed codepage, the codepage from the source is copied too. Otherwise a codepage conversion occurs.
        *
        * @param   value
        *          The source {@code longchar} to be assigned.
        */
       public void assign(longchar 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())
          {
             if (!isCodePageFixed())
             {
                codePage = value._getCodePage();
                super.assign(value);
             }
             else
             {
                super.assign(TextOps.codePageConvert(value, codePage, value._getCodePage()));
             }
          }
          else
          {
             super.assign(value);
             // do not fix code page if already done
             if (!isCodePageFixed())
             {
                fixCodePage(this, (String) null);
             }
          }
       }

       /**
        * Obtain the length (in bytes) of this BDT will use when serialized.
        *
        * @return  the length of this BDT will use when serialized.
        */
       @Override
       public int getSize()
       {
          return super.value.length();
       }
    }