# LargeObject.java

    /*
    ** Module   : LargeObject.java
    ** Abstract : Interface for objects which can be manipulated as LOBs
    **
    ** Copyright (c) 2018-2021, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
    ** 001 ECF 20181214 Created first version.
    ** 002 ECF 20190628 Implemented COPY-LOB runtime.
    ** 003 CA  20191203 Fixed overlay and unknown COPY-LOB related bugs.
    ** 004 OM  20210225 Pulled up binaryEquals() from clob.
    **     OM  20210312 Added parent FieldReference accessors in case the LOB was obtained by dereferenciation
    **                  of a buffer field.
    */

    /*
    ** 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.p2j.persist.*;
    import java.util.*;

    /**
     * An interface which must be implemented by classes which can be manipulated like LOBs.
     *
     * @see  com.goldencode.p2j.util.LobCopy
     */
    public interface LargeObject
    {
       /**
        * Indicate whether the object represents unknown value.
        *
        * @return  {@code true} if unknown value, else {@code false}.
        */
       public boolean isUnknown();

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

       /**
        * Return the specified range of the current buffer as an array. This may be a copy of the
        * data, depending on the implementation. DO NOT MODIFY the data that is returned, since it
        * is undefined as to whether the changes will or will not be reflected in the actual storage.
        *
        * @param   pos
        *          Starting offset position.
        * @param   len
        *          Length of range to return.
        *
        * @return  The contents of the instance (possibly a copy). 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.
        */
       public byte[] asByteArray(long pos, long len);

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

       /**
        * 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();

       /**
        * Get this object's code page, if it was explicitly overridden from the server's default.
        *
        * @return  This default implementation returns {@code null}.
        */
       public default String _getCodePage()
       {
          return null;
       }

       /**
        * Read an array of characters from this object, starting at the given offset.
        *
        * @param   offset
        *          Zero-based offset, in {@code char}s, at which to start reading.
        * @param   length
        *          Number of characters to read.
        *
        * @return  This default implementation returns {@code null}. It should be overridden by
        *          concrete subclasses.
        *
        * @throws  UnsupportedOperationException
        *          if invoked on a binary data object.
        */
       public default char[] readChars(int offset, int length)
       {
          if (!isCharacterData())
          {
             throw new UnsupportedOperationException();
          }

          return null;
       }

       /**
        * Read an array of bytes from this object, starting at the given offset.
        *
        * @param   offset
        *          Zero-based offset, in {@code byte}s, at which to start reading.
        * @param   length
        *          Number of bytes to read.
        *
        * @return  This default implementation returns {@code null}. It should be overridden by
        *          concrete subclasses.
        *
        * @throws  UnsupportedOperationException
        *          if invoked on a binary data object.
        */
       public default byte[] readBytes(int offset, int length)
       {
          if (isCharacterData())
          {
             throw new UnsupportedOperationException();
          }

          return null;
       }

       /**
        * Write the given array of bytes into the data managed by this object, overwriting the bytes
        * that are at the given location, if any.
        * <p>
        * This default implementation does nothing. It should be overridden by concrete subclasses.
        *
        * @param   overlay
        *          Flag indicating if the OVERLAY option is used.
        * @param   data
        *          Data to be assigned to this object.
        * @param   offset
        *          Offset position (in bytes) 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. It is up to the concrete implementation as
        *          to whether or not to ignore this option.
        */
       public default void write(boolean overlay, byte[] data, int offset, boolean trim)
       {
          if (isCharacterData())
          {
             throw new UnsupportedOperationException();
          }
       }

       /**
        * 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 default implementation does nothing. It should be overridden by concrete subclasses.
        *
        * @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. It is up to the concrete implementation as
        *          to whether or not to ignore this option.
        */
       public default void write(boolean overlay, String data, int offset, boolean trim)
       {
          if (!isCharacterData())
          {
             throw new UnsupportedOperationException();
          }
       }

       /**
        * Check two large objects for binary equality. They are equal if either both are {@code null} or
        * {@code unknown}, or if their data as byte arrays exactly match. Otherwise, they are unequal.
        *
        * @param   op1
        *          Left operand.
        * @param   op2
        *          Right operand.
        *
        * @return  {@code true} if the operands are binary equals, else {@code false}.
        */
       public static boolean binaryEquals(LargeObject op1, LargeObject op2)
       {
          boolean u1 = op1 == null || op1.isUnknown();
          boolean u2 = op2 == null || op2.isUnknown();

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

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

          return Arrays.equals(op1.asByteArray(0, op1.lengthOf()), op2.asByteArray(0, op2.lengthOf()));
       }

       /**
        * Sets the {@code FieldReference} reference. This is only used when the object is obtained by a
        * dereferenciation ({@code ::}) operation.
        *
        * @param   bf
        *          The source of this LOB.
        */
       public default void setFieldReference(FieldReference bf)
       {
          throw new UnsupportedOperationException("This LOB does not support dereferenciation.");
       }

       /**
        * Obtain the {@code FieldReference} reference, if any.
        *
        * @return  The parent {@code FieldReference} from which this LOB was obtained by dereferenciation.
        */
       public default FieldReference getFieldReference()
       {
          return null;
       }
    }